16 Commits

6 changed files with 226 additions and 74 deletions

1
.gitignore vendored
View File

@@ -27,3 +27,4 @@ bin/
### Mac OS ### ### Mac OS ###
.DS_Store .DS_Store
/firestar.zip

19
.idea/artifacts/firestar.xml generated Normal file
View File

@@ -0,0 +1,19 @@
<component name="ArtifactManager">
<artifact type="jpackage" name="firestar">
<output-path>$PROJECT_DIR$/out/artifacts/firestar</output-path>
<properties id="jpackage-properties">
<options>
<option name="version" value="natasha" />
<option name="copyright" value="(C) 2024 bonkmaykr" />
<option name="description" value="" />
<option name="vendor" value="Anti-Gravity Racing Federation" />
<option name="mainClass" value="Main" />
</options>
</properties>
<root id="root">
<element id="archive" name="firestar.jar">
<element id="module-output" name="firestar" />
</element>
</root>
</artifact>
</component>

View File

@@ -2,17 +2,15 @@
Firestar is a mod manager for WipEout 2048 which automatically handles sorting mods by priority and repacking game assets based on selected add-on packs. It runs on a desktop/laptop computer and aims to allow easy installation of mods for users who have only a surface level understanding of hacking the PSVita. Firestar is a mod manager for WipEout 2048 which automatically handles sorting mods by priority and repacking game assets based on selected add-on packs. It runs on a desktop/laptop computer and aims to allow easy installation of mods for users who have only a surface level understanding of hacking the PSVita.
# about 85% complete # about 95% complete
**What works:** **What works:**
- Importing & deleting mods - Managing mods (install, add, delete, toggle on/off)
- Repacking (installing) mods
- config loading
- Cross-platform support - Cross-platform support
**What doesn't work yet:** **What doesn't work yet:**
- change load order, toggle
- settings screen - settings screen
- misc. tools (pack creator, soundtrack mod generator...) - misc. tools (pack creator, soundtrack mod generator...)
- initial setup screen can still be a bit arduous for vita newbies
![](https://files.worlio.com/users/bonkmaykr/http/git/embed/firestar_window.png) ![](https://files.worlio.com/users/bonkmaykr/http/git/embed/firestar_window.png)

View File

@@ -17,6 +17,7 @@
*/ */
import net.lingala.zip4j.ZipFile; import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.util.FileUtils;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.swing.*; import javax.swing.*;
@@ -86,7 +87,7 @@ public class Gonzo {
final Thread managerThread = new Thread() { final Thread managerThread = new Thread() {
@Override @Override
public void run() { public void run() {
if (Main.repatch) { if (/*Main.repatch*/true) { //todo implement fast mode correctly or remove
CompatibilityRoutine(); CompatibilityRoutine();
} else { } else {
FastRoutine(); FastRoutine();
@@ -151,6 +152,7 @@ public class Gonzo {
// overwrite assets with custom ones from each mod and/or perform operations as specified in mod's delete list // 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 // todo: implement RegEx functions after delete.txt
for (Main.Mod m : Main.Mods) { for (Main.Mod m : Main.Mods) {
if (m.enabled) {
try { try {
System.out.println("Firestar is extracting " + m.friendlyName + " by " + m.author); System.out.println("Firestar is extracting " + m.friendlyName + " by " + m.author);
consoleDisplay.append("Firestar is extracting " + m.friendlyName + " by " + m.author + "\n"); consoleDisplay.append("Firestar is extracting " + m.friendlyName + " by " + m.author + "\n");
@@ -196,6 +198,7 @@ public class Gonzo {
return; return;
} }
} }
}
// create a list of the contents of data/ for psp2psarc.exe to read from // create a list of the contents of data/ for psp2psarc.exe to read from
List<String> oFilesList = new ArrayList<String>(); List<String> oFilesList = new ArrayList<String>();
@@ -272,6 +275,7 @@ public class Gonzo {
boolean one = new File(Main.outpath).mkdirs(); boolean one = new File(Main.outpath).mkdirs();
boolean two; boolean two;
System.out.println("created export folder: " + one); System.out.println("created export folder: " + one);
if (new File(Main.outpath + oArcTarget).exists()) {System.out.println("deleting existing file: " + Main.outpath + oArcTarget); new File(Main.outpath + oArcTarget).delete();} //hackjob
if (!Main.windows) {two = new File(System.getProperty("user.home") + "/.firestar/temp/" + oArcTarget).renameTo(new File(Main.outpath + oArcTarget));} 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));} 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); System.out.println("moved file to destination: " + two);
@@ -284,11 +288,9 @@ public class Gonzo {
return; return;
} }
try { try {
Process p; File tmp = new File(System.getProperty("user.home") + "/.firestar/temp/");
if (!Main.windows) {p = Runtime.getRuntime().exec(new String[]{"bash","-c","rm -rf " + System.getProperty("user.home") + "/.firestar/temp/"});} // Scary! deleteDir(tmp);
else {p = Runtime.getRuntime().exec(new String[]{"rmdir", new String(System.getProperty("user.home") + "/.firestar/temp/").replace("/", "\\").replace("\\", "\\\\"), "/s", "/q"});} } catch (Exception e) {
//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()); 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()); consoleDisplay.append("WARNING: Temporary files may not have been properly cleared.\n" + e.getMessage());
} }
@@ -343,4 +345,16 @@ public class Gonzo {
} }
} }
} }
private void deleteDir(File file) { // https://stackoverflow.com/a/29175213/9259829
File[] contents = file.listFiles();
if (contents != null) {
for (File f : contents) {
if (! Files.isSymbolicLink(f.toPath())) {
deleteDir(f);
}
}
}
file.delete();
}
} }

View File

@@ -48,6 +48,7 @@ public class Main {
public String game; //TODO for multi game support public String game; //TODO for multi game support
public int loaderversion = 0; //minimum required vint or feature level from Firestar public int loaderversion = 0; //minimum required vint or feature level from Firestar
public String author; // if null, "Author is unknown." public String author; // if null, "Author is unknown."
public boolean enabled = true;
} }
// Mods // Mods

View File

@@ -29,6 +29,8 @@ import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.*; import java.nio.file.*;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.ArrayList; import java.util.ArrayList;
@@ -72,6 +74,7 @@ public class MissPiggy implements ActionListener {
//private int selectedItem; //private int selectedItem;
public String priorityList; public String priorityList;
public String blackList;
public boolean listenersAlreadySet = false; // was written to troubleshoot a bug but this wasn't actually the cause public boolean listenersAlreadySet = false; // was written to troubleshoot a bug but this wasn't actually the cause
@@ -87,9 +90,10 @@ public class MissPiggy implements ActionListener {
toolsMenu = new JMenu("Tools"); toolsMenu = new JMenu("Tools");
helpMenu = new JMenu("Help"); helpMenu = new JMenu("Help");
fileMenu.add(new JMenuItem("Deploy All Mods")); fileMenu.add(new JMenuItem("Deploy Mods"));
fileMenu.add(new JMenuItem("Import Mod...")); fileMenu.add(new JMenuItem("Import Mod from File"));
fileMenu.add(new JMenuItem("Remove All")); //fileMenu.add(new JSeparator());
fileMenu.add(new JMenuItem("Delete All"));
fileMenu.add(new JSeparator()); fileMenu.add(new JSeparator());
fileMenu.add(new JMenuItem("Options")); fileMenu.add(new JMenuItem("Options"));
fileMenu.add(new JMenuItem("Quit")); fileMenu.add(new JMenuItem("Quit"));
@@ -99,6 +103,9 @@ public class MissPiggy implements ActionListener {
toolsMenu.add(new JMenuItem("Create Soundtrack Mod...")); toolsMenu.add(new JMenuItem("Create Soundtrack Mod..."));
//toolsMenu.add(new JMenuItem("Download Mod from URL")); // TODO: implement. move option to File menu. should be ez //toolsMenu.add(new JMenuItem("Download Mod from URL")); // TODO: implement. move option to File menu. should be ez
helpMenu.add(new JMenuItem("Source Code")); //replace with Website 'screwgravity.net' and 'Issue Tracker' gitea later
helpMenu.add(new JMenuItem("License"));
helpMenu.add(new JSeparator());
helpMenu.add(new JMenuItem("About Firestar")); helpMenu.add(new JMenuItem("About Firestar"));
menuBar.add(fileMenu); menuBar.add(fileMenu);
@@ -119,7 +126,10 @@ public class MissPiggy implements ActionListener {
fileMenu.getItem(5).addActionListener(this); fileMenu.getItem(5).addActionListener(this);
toolsMenu.getItem(0).addActionListener(this); toolsMenu.getItem(0).addActionListener(this);
toolsMenu.getItem(1).addActionListener(this); toolsMenu.getItem(1).addActionListener(this);
toolsMenu.getItem(2).addActionListener(this);
helpMenu.getItem(0).addActionListener(this); helpMenu.getItem(0).addActionListener(this);
helpMenu.getItem(1).addActionListener(this);
helpMenu.getItem(3).addActionListener(this);
deployButton.addActionListener(this); deployButton.addActionListener(this);
importButton.addActionListener(this); importButton.addActionListener(this);
@@ -181,11 +191,14 @@ public class MissPiggy implements ActionListener {
06/29/24 - also skip files that were manually removed but remain in the list 06/29/24 - also skip files that were manually removed but remain in the list
*/ */
File mod = new File(System.getProperty("user.home") + "/.firestar/mods/" + s.substring(s.indexOf("=") + 1).trim());
if (s.split("=")[0].matches("[0-9]+=*") && if (s.split("=")[0].matches("[0-9]+=*") &&
new File(System.getProperty("user.home") + "/.firestar/mods/" + s.substring(s.indexOf("=") + 1)).exists()) { mod.exists()) {
//append mod to list from substring //append mod to list from substring
Main.Mod m = new Main().new Mod(); Main.Mod m = new Main().new Mod();
m.path = s.substring(s.indexOf("=") + 1); m.path = s.substring(s.indexOf("=") + 1).trim();
System.out.println("found file " + m.path); System.out.println("found file " + m.path);
//get json metadata from zip comment //get json metadata from zip comment
@@ -203,11 +216,39 @@ public class MissPiggy implements ActionListener {
Main.Mods.add(m); Main.Mods.add(m);
} catch (Exception e) { } catch (Exception e) {
System.out.println("WARNING: mod entry for " + s + " was found but does not contain valid JSON metadata. skipping"); System.out.println("WARNING: mod entry for " + s + " was found but does not contain valid JSON metadata. skipping");
System.out.println(e.getMessage());
} }
} else { } else {
if (!s.isEmpty()) {System.out.println("WARNING: mod entry for " + s + " doesn't actually exist. skipping");} if (!s.isEmpty()) {System.out.println("WARNING: mod entry for " + s + " doesn't actually exist. skipping");}
} }
} }
try {
blackList = new String(Files.readAllBytes(Paths.get(System.getProperty("user.home") + "/.firestar/mods/blacklist")));
} catch (IOException e) {
File blackListFileHandle = new File(System.getProperty("user.home") + "/.firestar/mods/blacklist");
new File(System.getProperty("user.home") + "/.firestar/mods/").mkdirs();
if(!blackListFileHandle.isFile()){
try {
blackListFileHandle.createNewFile();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
blackList = "";
}
// initialize data structures from each list entry
String[] bListArray = blackList.split("\n");
//Arrays.sort(bListArray);
System.out.println("Initializing blacklist from file with length of " + bListArray.length + " units"); //debug
for (String s : bListArray) {
for (Main.Mod m : Main.Mods) {
if (s.trim().equals(m.path)) {
m.enabled = false;
}
}
}
} }
public void InitializeModListInGUI() { public void InitializeModListInGUI() {
@@ -224,7 +265,8 @@ public class MissPiggy implements ActionListener {
/*JLabel[]*/String[] contents = new String[Main.Mods.size()]; /*JLabel[]*/String[] contents = new String[Main.Mods.size()];
System.out.println("Initializing modList to GUI with length of " + Main.Mods.size() + " units"); //debug System.out.println("Initializing modList to GUI with length of " + Main.Mods.size() + " units"); //debug
while (i < Main.Mods.size()) { while (i < Main.Mods.size()) {
contents[i] = Main.Mods.get(i).friendlyName; if (Main.Mods.get(i).enabled) {contents[i] = Main.Mods.get(i).friendlyName;}
else {contents[i] = Main.Mods.get(i).friendlyName + " (Disabled)";}
//debug //debug
String authorDisplay; String authorDisplay;
@@ -255,15 +297,43 @@ public class MissPiggy implements ActionListener {
if (actionEvent.getSource() == moveUpButton) {moveUp(modList.getSelectedIndex());} else if (actionEvent.getSource() == moveUpButton) {moveUp(modList.getSelectedIndex());} else
if (actionEvent.getSource() == moveDownButton) {moveDown(modList.getSelectedIndex());} else if (actionEvent.getSource() == moveDownButton) {moveDown(modList.getSelectedIndex());} else
if (actionEvent.getSource() == toggleButton) {throwUnimplemented();} else // todo if (actionEvent.getSource() == toggleButton) {toggleSelected(modList.getSelectedIndex());} else
if (actionEvent.getSource() == deleteButton1) {deleteSelected();} else if (actionEvent.getSource() == deleteButton1) {deleteSelected();} else
if (actionEvent.getSource() == helpMenu.getItem(0)) {new Rowlf().displayAboutScreen();} if (actionEvent.getSource() == toolsMenu.getItem(0)) {throwUnimplemented();} else
if (actionEvent.getSource() == toolsMenu.getItem(1)) {throwUnimplemented();} else
if (actionEvent.getSource() == toolsMenu.getItem(2)) {throwUnimplemented();} else
if (actionEvent.getSource() == helpMenu.getItem(0)) {
try {
Desktop.getDesktop().browse(new URI("https://git.worlio.com/bonkmaykr/firestar"));
} catch (Exception e) {
System.out.println(e.getMessage());
JOptionPane.showMessageDialog(frame, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
} else
if (actionEvent.getSource() == helpMenu.getItem(1)) {
try {
Desktop.getDesktop().browse(new URI("https://www.gnu.org/licenses/gpl-3.0.en.html"));
} catch (Exception e) {
System.out.println(e.getMessage());
JOptionPane.showMessageDialog(frame, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
} else
if (actionEvent.getSource() == helpMenu.getItem(3)) {new Rowlf().displayAboutScreen();}
} }
// Will likely split the below functions into separate classes to work with intellij GUI designer. // Will likely split the below functions into separate classes to work with intellij GUI designer.
public void deployModGUI() { public void deployModGUI() {
int i = 0;
for (Main.Mod m : Main.Mods) {
if (m.enabled) {i++;}
}
if (i > 0) {
int result = JOptionPane.showConfirmDialog(frame, "A new PSARC will be generated. This can take several minutes.\nDuring this time, your computer may be very busy or slow.\n\nAre you sure you want to continue?", "Deploy Mods", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
if (result == JOptionPane.YES_OPTION) {
// prevent interruptions // prevent interruptions
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
frame.setEnabled(false); frame.setEnabled(false);
@@ -271,6 +341,10 @@ public class MissPiggy implements ActionListener {
// start // start
new Gonzo().DeployMods(this); new Gonzo().DeployMods(this);
} }
} else {
JOptionPane.showMessageDialog(frame, "Please add at least one mod file to continue.", "Error", JOptionPane.ERROR_MESSAGE);
}
}
public void wrapUpDeployment() { public void wrapUpDeployment() {
// restore functionality to main window // restore functionality to main window
@@ -325,13 +399,14 @@ public class MissPiggy implements ActionListener {
for (Main.Mod entry : Main.Mods) { for (Main.Mod entry : Main.Mods) {
new File(System.getProperty("user.home") + "/.firestar/mods/" + entry.path).delete(); new File(System.getProperty("user.home") + "/.firestar/mods/" + entry.path).delete();
} }
}
new File(System.getProperty("user.home") + "/.firestar/mods/index").delete(); new File(System.getProperty("user.home") + "/.firestar/mods/index").delete();
Main.Mods.clear(); Main.Mods.clear();
InitializeModListStructure(); InitializeModListStructure();
InitializeModListInGUI(); InitializeModListInGUI();
} }
}
public void optionsGUI() { public void optionsGUI() {
// todo settings page w/ reset switch // todo settings page w/ reset switch
@@ -339,11 +414,15 @@ public class MissPiggy implements ActionListener {
} }
public void deleteSelected() { public void deleteSelected() {
if (modList.getSelectedIndex() >= 0) {
File file = new File(System.getProperty("user.home") + "/.firestar/mods/" + Main.Mods.get(modList.getSelectedIndex()).path); File file = new File(System.getProperty("user.home") + "/.firestar/mods/" + Main.Mods.get(modList.getSelectedIndex()).path);
file.delete(); file.delete();
System.out.println("Deleted " + Main.Mods.get(modList.getSelectedIndex()).friendlyName); //debug System.out.println("Deleted " + Main.Mods.get(modList.getSelectedIndex()).friendlyName); //debug
Main.Mods.remove(modList.getSelectedIndex()); Main.Mods.remove(modList.getSelectedIndex());
regenerateModIndex(true); regenerateModIndex(true);
} else {
JOptionPane.showMessageDialog(frame, "Please select a mod to delete first.", "Error", JOptionPane.ERROR_MESSAGE);
}
} }
public void generatorGUI() { public void generatorGUI() {
@@ -374,8 +453,17 @@ public class MissPiggy implements ActionListener {
} }
} }
private void toggleSelected(int index) {
if (index >= 0) {
Main.Mods.get(index).enabled = !Main.Mods.get(index).enabled;
regenerateModBlacklist(true);
} else {
JOptionPane.showMessageDialog(frame, "Please select a mod to toggle first.", "Error", JOptionPane.ERROR_MESSAGE);
}
}
public void throwUnimplemented() { public void throwUnimplemented() {
JOptionPane.showMessageDialog(frame, "Unimplemented.\nSee README at https://git.worlio.com/bonkmaykr/firestar", "Unimplemented", JOptionPane.INFORMATION_MESSAGE); JOptionPane.showMessageDialog(frame, "This feature is unimplemented and will be coming soon.\nSee README at https://git.worlio.com/bonkmaykr/firestar", "Unimplemented", JOptionPane.INFORMATION_MESSAGE);
} }
public void createSelectionEventListener() { // moved incase needs to be removed and re-added public void createSelectionEventListener() { // moved incase needs to be removed and re-added
@@ -439,7 +527,6 @@ public class MissPiggy implements ActionListener {
File priorityListFileHandle = new File(System.getProperty("user.home") + "/.firestar/mods/index"); File priorityListFileHandle = new File(System.getProperty("user.home") + "/.firestar/mods/index");
priorityListFileHandle.createNewFile(); priorityListFileHandle.createNewFile();
BufferedWriter bw = new BufferedWriter(new FileWriter(System.getProperty("user.home") + "/.firestar/mods/index", true)); BufferedWriter bw = new BufferedWriter(new FileWriter(System.getProperty("user.home") + "/.firestar/mods/index", true));
int i = 0; int i = 0;
for (Main.Mod m : Main.Mods) { for (Main.Mod m : Main.Mods) {
@@ -448,6 +535,7 @@ public class MissPiggy implements ActionListener {
i++; i++;
} }
bw.close(); bw.close();
System.out.println("Mod index file regenerated.");
if(reload) { if(reload) {
Main.Mods.clear(); //cleanup Main.Mods.clear(); //cleanup
@@ -455,7 +543,38 @@ public class MissPiggy implements ActionListener {
InitializeModListStructure(); InitializeModListStructure();
InitializeModListInGUI(); InitializeModListInGUI();
} }
System.out.println("Mod index file regenerated."); } catch (Exception e) {
System.out.println(e.getMessage());
JOptionPane.showMessageDialog(frame, "An error has occured.\n" + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
public void regenerateModBlacklist(boolean reload) {
try {
System.out.println("Regenerating blacklist..."); //debug
new File(System.getProperty("user.home") + "/.firestar/mods/blacklist").delete();
File blackListFileHandle = new File(System.getProperty("user.home") + "/.firestar/mods/blacklist");
blackListFileHandle.createNewFile();
BufferedWriter bw2 = new BufferedWriter(new FileWriter(System.getProperty("user.home") + "/.firestar/mods/blacklist", true));
int i2 = 0;
for (Main.Mod m : Main.Mods) {
if (!m.enabled) {
bw2.write(m.path);
bw2.newLine();
i2++;
}
}
bw2.close();
System.out.println("Mod blacklist file regenerated.");
if(reload) {
Main.Mods.clear(); //cleanup
blackList = "";
InitializeModListStructure();
InitializeModListInGUI();
}
} catch (Exception e) { } catch (Exception e) {
System.out.println(e.getMessage()); System.out.println(e.getMessage());
JOptionPane.showMessageDialog(frame, "An error has occured.\n" + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); JOptionPane.showMessageDialog(frame, "An error has occured.\n" + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);