2024-05-07 09:44:26 -04:00
/ *
* Firestar Mod Manager
* Copyright ( C ) 2024 bonkmaykr
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see https : //www.gnu.org/licenses/.
* /
2024-05-07 13:21:22 -04:00
import net.lingala.zip4j.ZipFile ;
import javax.imageio.ImageIO ;
2024-05-07 09:44:26 -04:00
import javax.swing.* ;
2024-05-07 13:21:22 -04:00
import javax.swing.border.TitledBorder ;
2024-05-07 09:44:26 -04:00
import java.awt.* ;
2024-05-07 13:21:22 -04:00
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 ;
2024-05-07 09:44:26 -04:00
public class Gonzo {
JFrame frame = new JFrame ( ) ;
private JPanel frameContainer ;
private JTextArea consoleDisplay ;
2024-05-07 13:21:22 -04:00
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
2024-05-07 09:44:26 -04:00
2024-05-07 13:21:22 -04:00
public void DeployMods ( MissPiggy inv ) {
invoker = inv ;
System . out . println ( " \ n \ nStarting mod deployment \ n \ n " ) ;
2024-05-07 09:44:26 -04:00
frame . add ( frameContainer ) ; // initialize window contents -- will be handled by IntelliJ IDEA
2024-05-07 13:21:22 -04:00
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 ) ) ;
2024-05-07 09:44:26 -04:00
frame . setTitle ( " Mod Installation " ) ;
frame . setResizable ( false ) ;
frame . setDefaultCloseOperation ( WindowConstants . DO_NOTHING_ON_CLOSE ) ;
frame . setLayout ( new GridLayout ( ) ) ;
frame . setLocationRelativeTo ( null ) ;
2024-05-07 13:21:22 -04:00
frame . setAlwaysOnTop ( true ) ;
2024-05-07 09:44:26 -04:00
frame . setVisible ( true ) ;
2024-05-07 13:21:22 -04:00
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 ( ) {
2024-05-08 15:10:00 -04:00
if ( Main . repatch ) {
2024-05-07 14:08:12 -04:00
CompatibilityRoutine ( ) ;
2024-05-07 13:21:22 -04:00
} else {
2024-05-07 14:08:12 -04:00
FastRoutine ( ) ;
2024-05-07 13:21:22 -04:00
}
}
} ;
managerThread . start ( ) ;
}
2024-05-07 14:08:12 -04:00
private void CompatibilityRoutine ( ) {
2024-05-07 13:21:22 -04:00
// create temporary working area for asset dump
new File ( System . getProperty ( " user.home " ) + " /.firestar/temp/ " ) . mkdirs ( ) ;
// decide which files to dump
List < String > dumpThese = new ArrayList < String > ( ) ;
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
2024-05-08 05:51:25 -04:00
Process p ;
2024-05-08 08:17:26 -04:00
if ( ! Main . windows ) { p = Runtime . getRuntime ( ) . exec ( new String [ ] { " bash " , " -c " , " cd " + System . getProperty ( " user.home " ) + " /.firestar/temp/ " + " ;wine ../psp2psarc.exe extract -y ../ " + s } ) ; }
else { p = Runtime . getRuntime ( ) . exec ( new String [ ] { new String ( System . getProperty ( " user.home " ) + " \\ .firestar \\ psp2psarc.exe " ) , " extract " , " -y " , " .. \\ " + s } , null , new File ( new String ( System . getProperty ( " user.home " ) + " /.firestar/temp/ " ) . replace ( " / " , " \\ " ) ) ) ; }
2024-05-07 13:21:22 -04:00
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 ( ) ) ; }
2024-05-08 15:10:00 -04:00
catch ( Exception e ) { System . out . println ( " WARNING: Swing failed to paint window due to race condition. You can safely ignore this. \ n " + e . getMessage ( ) ) ; }
2024-05-07 13:21:22 -04:00
}
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 ( ) ) ;
2024-05-08 08:17:26 -04:00
consoleDisplay . append ( " CRITICAL FAILURE: " + e . getMessage ( ) ) ;
2024-05-08 15:10:00 -04:00
JOptionPane . showMessageDialog ( this . frame , " CRITICAL FAILURE: " + e . getMessage ( ) , " Fatal Error " , JOptionPane . ERROR_MESSAGE ) ;
2024-05-07 13:21:22 -04:00
frame . setDefaultCloseOperation ( WindowConstants . DO_NOTHING_ON_CLOSE ) ;
AllowExit ( ) ;
2024-05-08 08:17:26 -04:00
return ;
2024-05-07 13:21:22 -04:00
}
}
// 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 " ) ) ) ;
2024-05-08 08:17:26 -04:00
if ( Main . windows ) { deleteQueue = new String ( Files . readAllBytes ( Paths . get ( System . getProperty ( " user.home " ) + " \\ .firestar \\ temp \\ delete.txt " ) ) ) ; } // might be unnecessary
2024-05-07 13:21:22 -04:00
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 {
2024-05-08 08:17:26 -04:00
if ( ! Main . windows ) {
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 ( ) ; }
else {
System . out . println ( " Deleting " + new String ( System . getProperty ( " user.home " ) + " \\ .firestar \\ temp \\ data " + file ) . replace ( " / " , " \\ " ) ) ;
consoleDisplay . append ( " Deleting " + new String ( System . getProperty ( " user.home " ) + " \\ .firestar \\ temp \\ data " + file ) . replace ( " / " , " \\ " ) + " \ n " ) ;
new File ( new String ( System . getProperty ( " user.home " ) + " \\ .firestar \\ temp \\ data " + file ) . replace ( " / " , " \\ " ) ) . delete ( ) ;
}
2024-05-07 13:21:22 -04:00
}
}
2024-05-08 08:21:53 -04:00
// cleanup so we don't run it again for the next mod unless needed
// this is not necessary but good practice
new File ( System . getProperty ( " user.home " ) + " /.firestar/temp/delete.txt " ) . delete ( ) ;
2024-05-07 13:21:22 -04:00
}
2024-05-08 08:17:26 -04:00
} catch ( Exception e ) {
2024-05-07 13:21:22 -04:00
System . out . println ( e . getMessage ( ) ) ;
2024-05-08 08:17:26 -04:00
consoleDisplay . append ( " CRITICAL FAILURE: " + e . getMessage ( ) ) ;
2024-05-08 15:10:00 -04:00
JOptionPane . showMessageDialog ( this . frame , " CRITICAL FAILURE: " + e . getMessage ( ) , " Fatal Error " , JOptionPane . ERROR_MESSAGE ) ;
2024-05-07 13:21:22 -04:00
frame . setDefaultCloseOperation ( WindowConstants . DO_NOTHING_ON_CLOSE ) ;
AllowExit ( ) ;
2024-05-08 08:17:26 -04:00
return ;
2024-05-07 13:21:22 -04:00
}
}
// create a list of the contents of data/ for psp2psarc.exe to read from
List < String > oFilesList = new ArrayList < String > ( ) ;
List < String > oFilesList2 = new ArrayList < String > ( ) ;
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.
2024-05-08 08:17:26 -04:00
if ( ! Main . windows ) { oFilesList2 . add ( p . replace ( " \\ " , " / " ) . split ( new String ( System . getProperty ( " user.home " ) + " /.firestar/temp/ " ) ) [ 1 ] ) ; }
else { oFilesList2 . add ( p . split ( new String ( System . getProperty ( " user.home " ) + " \\ .firestar \\ temp \\ " ) . replace ( " \\ " , " \\ \\ " ) ) [ 1 ] ) ; } // path wont match regex unless adjusted for windows here
2024-05-07 13:21:22 -04:00
}
//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 ( ) ;
2024-05-08 08:17:26 -04:00
} catch ( Exception e ) {
2024-05-07 13:21:22 -04:00
System . out . println ( e . getMessage ( ) ) ;
2024-05-08 08:17:26 -04:00
consoleDisplay . append ( " CRITICAL FAILURE: " + e . getMessage ( ) ) ;
2024-05-08 15:10:00 -04:00
JOptionPane . showMessageDialog ( this . frame , " CRITICAL FAILURE: " + e . getMessage ( ) , " Fatal Error " , JOptionPane . ERROR_MESSAGE ) ;
2024-05-07 13:21:22 -04:00
frame . setDefaultCloseOperation ( WindowConstants . DO_NOTHING_ON_CLOSE ) ;
AllowExit ( ) ;
2024-05-08 08:17:26 -04:00
return ;
2024-05-07 13:21:22 -04:00
}
// 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 " ) ;
2024-05-08 05:51:25 -04:00
Process p ;
2024-05-08 08:17:26 -04:00
if ( ! Main . windows ) { p = Runtime . getRuntime ( ) . exec ( new String [ ] { " bash " , " -c " , " cd " + System . getProperty ( " user.home " ) + " /.firestar/temp " + " ;wine ../psp2psarc.exe create --skip-missing-files -j12 -a -i --input-file=list.txt -o " + oArcTarget } ) ; }
else { p = Runtime . getRuntime ( ) . exec ( new String [ ] { new String ( System . getProperty ( " user.home " ) + " \\ .firestar \\ psp2psarc.exe " ) , " create " , " --skip-missing-files " , " -j12 " , " -a " , " -i " , " --input-file=list.txt " , " -o " + oArcTarget } , null , new File ( new String ( System . getProperty ( " user.home " ) + " /.firestar/temp/ " ) . replace ( " / " , " \\ " ) ) ) ; }
2024-05-07 13:21:22 -04:00
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 ( ) ) ;
2024-05-08 08:17:26 -04:00
consoleDisplay . append ( " CRITICAL FAILURE: " + e . getMessage ( ) ) ;
2024-05-08 15:10:00 -04:00
JOptionPane . showMessageDialog ( this . frame , " CRITICAL FAILURE: " + e . getMessage ( ) , " Fatal Error " , JOptionPane . ERROR_MESSAGE ) ;
2024-05-07 13:21:22 -04:00
frame . setDefaultCloseOperation ( WindowConstants . DO_NOTHING_ON_CLOSE ) ;
AllowExit ( ) ;
2024-05-08 08:17:26 -04:00
return ;
2024-05-07 13:21:22 -04:00
}
// cleanup
2024-05-08 15:15:00 -04:00
boolean one = new File ( Main . outpath ) . mkdirs ( ) ;
boolean two ;
System . out . println ( " created export folder: " + one ) ;
if ( ! Main . windows ) { two = new File ( System . getProperty ( " user.home " ) + " /.firestar/temp/ " + oArcTarget ) . renameTo ( new File ( Main . outpath + oArcTarget ) ) ; }
else { two = new File ( System . getProperty ( " user.home " ) + " \\ .firestar \\ temp \\ " + oArcTarget ) . renameTo ( new File ( Main . outpath + oArcTarget ) ) ; }
System . out . println ( " moved file to destination: " + two ) ;
if ( one & & two ) { System . out . println ( " file should be located at " + Main . outpath + oArcTarget ) ; } else {
System . out . println ( " CRITICAL FAILURE: Please check that your output path is correct and that you have write permissions! " ) ;
consoleDisplay . append ( " CRITICAL FAILURE: Please check that your output path is correct and that you have write permissions! " ) ;
JOptionPane . showMessageDialog ( this . frame , " CRITICAL FAILURE: Please check that your output path is correct and that you have write permissions! " , " Fatal Error " , JOptionPane . ERROR_MESSAGE ) ;
frame . setDefaultCloseOperation ( WindowConstants . DO_NOTHING_ON_CLOSE ) ;
AllowExit ( ) ;
return ;
}
2024-05-07 13:21:22 -04:00
try {
2024-05-08 05:51:25 -04:00
Process p ;
2024-05-08 08:17:26 -04:00
if ( ! Main . windows ) { p = Runtime . getRuntime ( ) . exec ( new String [ ] { " bash " , " -c " , " rm -rf " + System . getProperty ( " user.home " ) + " /.firestar/temp/ " } ) ; } // Scary!
else { p = Runtime . getRuntime ( ) . exec ( new String [ ] { " rmdir " , new String ( System . getProperty ( " user.home " ) + " /.firestar/temp/ " ) . replace ( " / " , " \\ " ) . replace ( " \\ " , " \\ \\ " ) , " /s " , " /q " } ) ; }
2024-05-07 13:21:22 -04:00
//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 ( ) ;
}
2024-05-07 14:08:12 -04:00
private void FastRoutine ( ) {
2024-05-07 13:21:22 -04:00
}
public void AllowExit ( ) {
System . out . println ( " \ n \ nYou may now close the pop-up window. " ) ;
consoleDisplay . append ( " \ n \ n \ nYou may now close the pop-up window. " ) ;
2024-05-08 15:10:00 -04:00
try { TimeUnit . MILLISECONDS . sleep ( 200 ) ; } catch ( InterruptedException e ) { } //ignore
2024-05-07 14:08:12 -04:00
scrollPane . getVerticalScrollBar ( ) . setValue ( scrollPane . getVerticalScrollBar ( ) . getMaximum ( ) ) ;
2024-05-07 13:21:22 -04:00
frame . addWindowListener ( new WindowAdapter ( ) {
@Override
public void windowClosing ( WindowEvent e )
{
invoker . wrapUpDeployment ( ) ;
e . getWindow ( ) . dispose ( ) ;
}
} ) ;
}
private static void listAllFiles ( Path currentPath , List < String > allFiles )
throws IOException
{
try ( DirectoryStream < Path > stream = Files . newDirectoryStream ( currentPath ) )
{
for ( Path entry : stream ) {
if ( Files . isDirectory ( entry ) ) {
listAllFiles ( entry , allFiles ) ;
} else {
allFiles . add ( entry . toString ( ) ) ;
}
}
}
2024-05-07 09:44:26 -04:00
}
}