diff --git a/src/Gonzo.form b/src/Gonzo.form index 1414db7..6227570 100644 --- a/src/Gonzo.form +++ b/src/Gonzo.form @@ -8,16 +8,21 @@ - + - - - + - - - - + + + + + + + + + + + diff --git a/src/Gonzo.java b/src/Gonzo.java index 55dda57..3402994 100644 --- a/src/Gonzo.java +++ b/src/Gonzo.java @@ -16,25 +16,291 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ +import net.lingala.zip4j.ZipFile; +import net.lingala.zip4j.util.FileUtils; + +import javax.imageio.ImageIO; import javax.swing.*; +import javax.swing.border.TitledBorder; import java.awt.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; +import java.io.*; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; public class Gonzo { JFrame frame = new JFrame(); private JPanel frameContainer; private JTextArea consoleDisplay; + private JScrollPane scrollPane; + public boolean data0; + public boolean data1; + public boolean data2; + public boolean dlc1; + public boolean dlc2; + private MissPiggy invoker; + public String oArcTarget = "dlc2.psarc"; // which psarc to rebuild the assets in - public void DeployMods() { + public void DeployMods(MissPiggy inv) { + invoker = inv; + System.out.println("\n\nStarting mod deployment\n\n"); frame.add(frameContainer); // initialize window contents -- will be handled by IntelliJ IDEA - frame.setSize(200, 100); - frame.setMinimumSize(new Dimension(200,100)); + try { + BufferedImage windowIcon = ImageIO.read(new File(System.getProperty("user.dir") + "/resources/titleIcon.png")); + frame.setIconImage(windowIcon); + } catch (IOException e) { + System.out.println("ERROR: Failed to find /resources/titleIcon.png. Window will not have an icon."); + } + frame.setSize(800, 400); + frame.setMinimumSize(new Dimension(600,400)); frame.setTitle("Mod Installation"); frame.setResizable(false); frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); frame.setLayout(new GridLayout()); frame.setLocationRelativeTo(null); + frame.setAlwaysOnTop(true); frame.setVisible(true); + + File psarcHandle = new File(Main.inpath + "data.psarc"); + data0 = psarcHandle.isFile(); + psarcHandle = new File(Main.inpath + "data1.psarc"); + data1 = psarcHandle.isFile(); + psarcHandle = new File(Main.inpath + "data2.psarc"); + data2 = psarcHandle.isFile(); + psarcHandle = new File(Main.inpath + "dlc1.psarc"); + dlc1 = psarcHandle.isFile(); + psarcHandle = new File(Main.inpath + "dlc2.psarc"); + dlc2 = psarcHandle.isFile(); + + System.out.println("Source files discovered: data " + data0 + ", data1 " + data1 + ", data2 " + data2 + ", dlc1 " + dlc1 + ", dlc2 " + dlc2); + + final Thread managerThread = new Thread() { + @Override + public void run() { + if (!Main.wine) { + POSIXRoutine(); + } else { + Win32Routine(); + } + } + }; + managerThread.start(); + } + + private void POSIXRoutine() { + // create temporary working area for asset dump + new File(System.getProperty("user.home") + "/.firestar/temp/").mkdirs(); + + // decide which files to dump + List dumpThese = new ArrayList(); + if (data0) {dumpThese.add("data.psarc");oArcTarget = "data.psarc";} + if (data1) {dumpThese.add("data1.psarc");oArcTarget = "data1.psarc";} + if (data2) {dumpThese.add("data2.psarc");oArcTarget = "data2.psarc";} + if (dlc1) {dumpThese.add("dlc1.psarc");oArcTarget = "dlc1.psarc";} + if (dlc2) {dumpThese.add("dlc2.psarc");oArcTarget = "dlc2.psarc";} + + // dump all assets to working area + for (String s : dumpThese) { + try { + System.out.println("Firestar is extracting " + s); + consoleDisplay.append("Firestar is extracting " + s + "\n"); + //Process p = Runtime.getRuntime().exec(new String[]{"bash","-c","aplay /home/bonkyboo/kittens_loop.wav"}); // DEBUG + Process p = Runtime.getRuntime().exec(new String[]{"bash","-c","cd " + System.getProperty("user.home") + "/.firestar/temp/" + ";wine ../psp2psarc.exe extract -y ../" + s}); + final Thread ioThread = new Thread() { + @Override + public void run() { + try { + final BufferedReader reader = new BufferedReader( + new InputStreamReader(p.getInputStream())); + String line = null; + while ((line = reader.readLine()) != null) { + System.out.println(line); + consoleDisplay.append(line + "\n"); + try {scrollPane.getVerticalScrollBar().setValue(scrollPane.getVerticalScrollBar().getMaximum());} + catch (Exception e) {System.out.println("WARNING: Swing failed to paint window due to race condition.\n" + e.getMessage());} + } + reader.close(); + } catch (final Exception e) { + e.printStackTrace(); // will probably definitely absolutely for sure hang firestar unless we do something. Too bad! + } + } + }; + ioThread.start(); + p.waitFor(); + } catch (IOException | InterruptedException e) { + System.out.println(e.getMessage()); + frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + AllowExit(); + break; + } + } + + // overwrite assets with custom ones from each mod and/or perform operations as specified in mod's delete list + // todo: implement RegEx functions after delete.txt + for (Main.Mod m : Main.Mods) { + try { + System.out.println("Firestar is extracting " + m.friendlyName + " by " + m.author); + consoleDisplay.append("Firestar is extracting " + m.friendlyName + " by " + m.author + "\n"); + new ZipFile(System.getProperty("user.home") + "/.firestar/mods/" + m.path).extractAll(System.getProperty("user.home") + "/.firestar/temp/"); + + if (new File(System.getProperty("user.home") + "/.firestar/temp/delete.txt").isFile()) { + System.out.println("Firestar is deleting files that conflict with " + m.friendlyName + " by " + m.author); + consoleDisplay.append("Firestar is deleting files that conflict with " + m.friendlyName + " by " + m.author + "\n"); + + String deleteQueue = new String(Files.readAllBytes(Paths.get(System.getProperty("user.home") + "/.firestar/temp/delete.txt"))); + String[] dQarray = deleteQueue.split("\n"); + Arrays.sort(dQarray); + System.out.println("The deletion queue is " + dQarray.length + " files long!"); //debug + + for (String file : dQarray) { + if(file.contains("..")) { //todo: find all possible hazardous paths and blacklist them with regex + System.out.println("WARNING: Firestar skipped a potentially dangerous delete command. Please ensure the mod you're installing is from someone you trust!"); + consoleDisplay.append("WARNING: Firestar skipped a potentially dangerous delete command. Please ensure the mod you're installing is from someone you trust!\n"); + } else { + System.out.println("Deleting " + System.getProperty("user.home") + "/.firestar/temp/data/" + file); + consoleDisplay.append("Deleting " + System.getProperty("user.home") + "/.firestar/temp/data/" + file + "\n"); + new File(System.getProperty("user.home") + "/.firestar/temp/data/" + file).delete(); + } + } + } + } catch (IOException e) { + System.out.println(e.getMessage()); + frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + AllowExit(); + break; + } + } + + // create a list of the contents of data/ for psp2psarc.exe to read from + List oFilesList = new ArrayList(); + List oFilesList2 = new ArrayList(); + try { + listAllFiles(Paths.get(System.getProperty("user.home") + "/.firestar/temp/data/"), oFilesList); + for (String p : oFilesList) { + // We need to clean up the path here on Linux to avoid psp2psarc getting confused about where the hell "/" is. + // In WINE it should see it as Z: by default, but if it's somewhere else then I don't have an elegant way of knowing what drive letter it's on, so + // relative paths are kind of the only choice here. This can be extended to Windows too as it works there, though completely unnecessary. + oFilesList2.add(p.replace("\\", "/").split(System.getProperty("user.home") + "/.firestar/temp/data/")[1]); + } + //oFilesList2.forEach(System.out::println); //debug + File oFilesListO = new File(System.getProperty("user.home") + "/.firestar/temp/list.txt"); + if (oFilesListO.isFile()) {oFilesListO.delete();} + FileWriter oFilesListWr = new FileWriter(oFilesListO, true); + int i = 0; + for (String p : oFilesList2) { + oFilesListWr.append(p); + if (i != oFilesList2.size()) { + oFilesListWr.append("\n"); + } + i++; + } + oFilesListWr.close(); + } catch (IOException e) { + System.out.println(e.getMessage()); + frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + AllowExit(); + } + + // invoke psp2psarc.exe one final time to reconstruct the assets + try { + System.out.println("Firestar is compiling the final build"); + consoleDisplay.append("Firestar is compiling the final build" + "\n"); + Process p = Runtime.getRuntime().exec(new String[]{"bash","-c","cd " + System.getProperty("user.home") + "/.firestar/temp/data" + ";wine ../../psp2psarc.exe create --skip-missing-files -j12 -a -i --input-file=../list.txt -o ../" + oArcTarget}); + final Thread ioThread = new Thread() { + @Override + public void run() { + try { + final BufferedReader reader = new BufferedReader( + new InputStreamReader(p.getInputStream())); + String line = null; + while ((line = reader.readLine()) != null) { + System.out.println(line); + consoleDisplay.append(line + "\n"); + try {scrollPane.getVerticalScrollBar().setValue(scrollPane.getVerticalScrollBar().getMaximum());} + catch (Exception e) {System.out.println("WARNING: Swing failed to paint window due to race condition.\n" + e.getMessage());} + } + reader.close(); + } catch (final Exception e) { + e.printStackTrace(); // will probably definitely absolutely for sure hang firestar unless we do something. Too bad! + } + } + }; + ioThread.start(); + p.waitFor(); + } catch (IOException | InterruptedException e) { + System.out.println(e.getMessage()); + frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + AllowExit(); + } + + // cleanup + new File(Main.outpath).mkdirs(); + new File(System.getProperty("user.home") + "/.firestar/temp/" + oArcTarget).renameTo(new File(Main.outpath + oArcTarget)); + try { + Process p = Runtime.getRuntime().exec(new String[]{"bash","-c","rm -rf " + System.getProperty("user.home") + "/.firestar/temp/"}); // Scary! + //new File(System.getProperty("user.home") + "/.firestar/temp/").delete(); + } catch (IOException e) { + System.out.println("WARNING: Temporary files may not have been properly cleared.\n" + e.getMessage()); + consoleDisplay.append("WARNING: Temporary files may not have been properly cleared.\n" + e.getMessage()); + } + + // done! + try { + TimeUnit.SECONDS.sleep(1); // avoid race condition when logging + } catch (InterruptedException e) { + //ignore + } + + TitledBorder titledBorder = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.BLACK), "DONE! Close this pop-up to continue."); + titledBorder.setTitlePosition(TitledBorder.BOTTOM); + titledBorder.setTitleJustification(TitledBorder.CENTER); + scrollPane.setBorder(titledBorder); + scrollPane.repaint(); + + frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + AllowExit(); + } + + private void Win32Routine() { + + } + + public void AllowExit() { + scrollPane.getVerticalScrollBar().setValue(scrollPane.getVerticalScrollBar().getMaximum()); + System.out.println("\n\nYou may now close the pop-up window."); + consoleDisplay.append("\n\n\nYou may now close the pop-up window."); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) + { + invoker.wrapUpDeployment(); + e.getWindow().dispose(); + } + }); + } + + private static void listAllFiles(Path currentPath, List allFiles) + throws IOException + { + try (DirectoryStream stream = Files.newDirectoryStream(currentPath)) + { + for (Path entry : stream) { + if (Files.isDirectory(entry)) { + listAllFiles(entry, allFiles); + } else { + allFiles.add(entry.toString()); + } + } + } } } diff --git a/src/Main.java b/src/Main.java index 85d2852..f3a06a7 100644 --- a/src/Main.java +++ b/src/Main.java @@ -32,7 +32,9 @@ public class Main { public static final int vint = 0; // User Settings - public static String outpath; //game assets location + // TODO: replace with user preference when config i/o is done + // also please double check that outpath is actually valid + public static String outpath = System.getProperty("user.home") + "/.firestar/out/"; //game assets location public static String inpath = System.getProperty("user.home") + "/.firestar/"; //game assets location public static boolean repatch; //are we in compat mode? public static boolean wine; //are we on Linux, MINIX, BSD? diff --git a/src/MissPiggy.java b/src/MissPiggy.java index 1fd440d..17dfc48 100644 --- a/src/MissPiggy.java +++ b/src/MissPiggy.java @@ -265,7 +265,7 @@ public class MissPiggy implements ActionListener { frame.setEnabled(false); // start - new Gonzo().DeployMods(); + new Gonzo().DeployMods(this); } public void wrapUpDeployment() {