diff --git a/.envrc b/.envrc
new file mode 100644
index 0000000..8392d15
--- /dev/null
+++ b/.envrc
@@ -0,0 +1 @@
+use flake
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 2b925ad..9ab7d63 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,18 +5,19 @@ target/
*.bak
~$*
-
# Windows image file caches
Thumbs.db
# Windows folder config file
Desktop.ini
-
# Mac junk
.DS_Store
-
# IdeaJ
*.idea
*.iml
+
+# enviroemnt packages
+.direnv
+
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..353863f
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,25 @@
+{
+ "nodes": {
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1754725699,
+ "narHash": "sha256-iAcj9T/Y+3DBy2J0N+yF9XQQQ8IEb5swLFzs23CdP88=",
+ "rev": "85dbfc7aaf52ecb755f87e577ddbe6dbbdbc1054",
+ "revCount": 841808,
+ "type": "tarball",
+ "url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.1.841808%2Brev-85dbfc7aaf52ecb755f87e577ddbe6dbbdbc1054/01989280-4b63-70f9-95b3-49c511cb4d92/source.tar.gz"
+ },
+ "original": {
+ "type": "tarball",
+ "url": "https://flakehub.com/f/NixOS/nixpkgs/0.1"
+ }
+ },
+ "root": {
+ "inputs": {
+ "nixpkgs": "nixpkgs"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..2951bbe
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,69 @@
+{
+ description = "A Nix-flake-based Java development environment";
+
+ inputs.nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.1";
+
+ outputs =
+ inputs:
+ let
+ javaVersion = 17;
+
+ supportedSystems = [
+ "x86_64-linux"
+ "aarch64-linux"
+ "x86_64-darwin"
+ "aarch64-darwin"
+ ];
+ forEachSupportedSystem =
+ f:
+ inputs.nixpkgs.lib.genAttrs supportedSystems (
+ system:
+ f {
+ pkgs = import inputs.nixpkgs {
+ inherit system;
+ overlays = [ inputs.self.overlays.default ];
+ };
+ }
+ );
+ in
+ {
+ overlays.default =
+ final: prev:
+ let
+ jdk = prev."jdk${toString javaVersion}";
+ in
+ {
+ inherit jdk;
+ maven = prev.maven.override { jdk_headless = jdk; };
+ gradle = prev.gradle.override { java = jdk; };
+ lombok = prev.lombok.override { inherit jdk; };
+ };
+
+ devShells = forEachSupportedSystem (
+ { pkgs }:
+ {
+ default = pkgs.mkShell {
+ packages = with pkgs; [
+ gcc
+ gradle
+ jdk
+ maven
+ ncurses
+ patchelf
+ zlib
+ ];
+
+ shellHook =
+ let
+ loadLombok = "-javaagent:${pkgs.lombok}/share/java/lombok.jar";
+ prev = "\${JAVA_TOOL_OPTIONS:+ $JAVA_TOOL_OPTIONS}";
+ in
+ ''
+ export JAVA_TOOL_OPTIONS="${loadLombok}${prev}"
+ '';
+ };
+ }
+ );
+ };
+}
+
diff --git a/pom.xml b/pom.xml
index b235564..53264db 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,8 +11,8 @@
1.6
- 1.6
- 1.6
+ 21
+ 21
UTF-8
diff --git a/shell.nix b/shell.nix
new file mode 100644
index 0000000..d7c46b9
--- /dev/null
+++ b/shell.nix
@@ -0,0 +1,14 @@
+(
+ import
+ (
+ let
+ lock = builtins.fromJSON (builtins.readFile ./flake.lock);
+ in
+ fetchTarball {
+ url = lock.nodes.flake-compat.locked.url or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
+ sha256 = lock.nodes.flake-compat.locked.narHash;
+ }
+ )
+ {src = ./.;}
+)
+.shellNix
diff --git a/skel_unix/modman-cli.sh b/skel_unix/modman-cli.sh
index 00104c8..9c7124c 100644
--- a/skel_unix/modman-cli.sh
+++ b/skel_unix/modman-cli.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
# Get the absolute path to this script's folder.
if echo "$0" | awk '{exit(!/^\//);}'; then
diff --git a/skel_unix/modman.command b/skel_unix/modman.command
index 1381d8f..3b63900 100644
--- a/skel_unix/modman.command
+++ b/skel_unix/modman.command
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
# Get the script's name.
me=$(basename "$0");
diff --git a/src/main/java/net/vhati/modmanager/FTLModManager.java b/src/main/java/net/vhati/modmanager/FTLModManager.java
index b4fa76b..c431262 100644
--- a/src/main/java/net/vhati/modmanager/FTLModManager.java
+++ b/src/main/java/net/vhati/modmanager/FTLModManager.java
@@ -1,19 +1,14 @@
package net.vhati.modmanager;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.io.InputStreamReader;
import java.io.IOException;
-import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.util.Date;
-import java.util.Properties;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
-import javax.swing.UIManager.LookAndFeelInfo;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
@@ -36,8 +31,8 @@ public class FTLModManager {
public static final String APP_NAME = "Slipstream Mod Manager";
public static final ComparableVersion APP_VERSION = new ComparableVersion( "1.9.1" );
- public static final String APP_URL = "https://subsetgames.com/forum/viewtopic.php?f=12&t=17102";
- public static final String APP_AUTHOR = "Vhati";
+ public static final String APP_URL = "TODO";
+ public static final String APP_AUTHOR = "jan-leila";
public static void main( String[] args ) {
@@ -53,7 +48,7 @@ public class FTLModManager {
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setContext( lc );
- encoder.setCharset( Charset.forName( "UTF-8" ) );
+ encoder.setCharset(StandardCharsets.UTF_8);
encoder.setPattern( "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" );
encoder.start();
@@ -76,7 +71,7 @@ public class FTLModManager {
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException( Thread t, Throwable e ) {
- log.error( "Uncaught exception in thread: "+ t.toString(), e );
+ log.error("Uncaught exception in thread: {}", t.toString(), e);
}
});
@@ -86,20 +81,15 @@ public class FTLModManager {
}
// Ensure all popups are triggered from the event dispatch thread.
-
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- guiInit();
- }
- });
+ SwingUtilities.invokeLater(FTLModManager::guiInit);
}
private static void guiInit() {
try {
+ // TODO: get mods file from env var
// Nag if the jar was double-clicked.
- if ( new File( "./mods/" ).exists() == false ) {
+ if (!new File("./mods/").exists()) {
String currentPath = new File( "." ).getAbsoluteFile().getParentFile().getAbsolutePath();
log.error( String.format( "Slipstream could not find its own folder (Currently in \"%s\"), exiting...", currentPath ) );
@@ -108,46 +98,13 @@ public class FTLModManager {
throw new ExitException();
}
+ // TODO: get config file from env var
File configFile = new File( "modman.cfg" );
- boolean writeConfig = false;
- Properties props = new Properties();
- props.setProperty( SlipstreamConfig.ALLOW_ZIP, "false" );
- props.setProperty( SlipstreamConfig.FTL_DATS_PATH, "" ); // Prompt.
- props.setProperty( SlipstreamConfig.STEAM_DISTRO, "" ); // Prompt.
- props.setProperty( SlipstreamConfig.STEAM_EXE_PATH, "" ); // Prompt.
- props.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "" ); // Prompt.
- props.setProperty( SlipstreamConfig.NEVER_RUN_FTL, "false" );
- props.setProperty( SlipstreamConfig.UPDATE_CATALOG, "" ); // Prompt.
- props.setProperty( SlipstreamConfig.UPDATE_APP, "" ); // Prompt.
- props.setProperty( SlipstreamConfig.USE_DEFAULT_UI, "false" );
- props.setProperty( SlipstreamConfig.REMEMBER_GEOMETRY, "true" );
- // "manager_geometry" doesn't have a default.
-
- // Read the config file.
- InputStream in = null;
- try {
- if ( configFile.exists() ) {
- log.debug( "Loading config file" );
- in = new FileInputStream( configFile );
- props.load( new InputStreamReader( in, "UTF-8" ) );
- } else {
- writeConfig = true; // Create a new cfg, but only if necessary.
- }
- }
- catch ( IOException e ) {
- log.error( "Error loading config", e );
- showErrorDialog( "Error loading config from "+ configFile.getPath() );
- }
- finally {
- try {if ( in != null ) in.close();}
- catch ( IOException e ) {}
- }
-
- SlipstreamConfig appConfig = new SlipstreamConfig( props, configFile );
+ SlipstreamConfig appConfig = new SlipstreamConfig(configFile);
// Look-and-Feel.
- boolean useDefaultUI = "true".equals( appConfig.getProperty( SlipstreamConfig.USE_DEFAULT_UI, "false" ) );
+ boolean useDefaultUI = Boolean.parseBoolean(appConfig.getProperty(SlipstreamConfig.USE_DEFAULT_UI, "false"));
if ( !useDefaultUI ) {
LookAndFeel defaultLaf = UIManager.getLookAndFeel();
@@ -157,7 +114,7 @@ public class FTLModManager {
log.debug( "Setting system look and feel: "+ UIManager.getSystemLookAndFeelClassName() );
// SystemLaf is risky. It may throw an exception, or lead to graphical bugs.
- // Problems are geneally caused by custom Windows themes.
+ // Problems are generally caused by custom Windows themes.
UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
}
catch ( Exception e ) {
@@ -165,7 +122,6 @@ public class FTLModManager {
log.info( "Setting "+ SlipstreamConfig.USE_DEFAULT_UI +"=true in the config file to prevent this error..." );
appConfig.setProperty( SlipstreamConfig.USE_DEFAULT_UI, "true" );
- writeConfig = true;
try {
UIManager.setLookAndFeel( defaultLaf );
@@ -220,7 +176,6 @@ public class FTLModManager {
if ( datsDir != null ) {
appConfig.setProperty( SlipstreamConfig.FTL_DATS_PATH, datsDir.getAbsolutePath() );
- writeConfig = true;
log.info( "FTL dats located at: "+ datsDir.getAbsolutePath() );
}
}
@@ -237,11 +192,9 @@ public class FTLModManager {
int steamBasedResponse = JOptionPane.showConfirmDialog( null, "Was FTL installed via Steam?", "Confirm", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE );
if ( steamBasedResponse == JOptionPane.YES_OPTION ) {
appConfig.setProperty( SlipstreamConfig.STEAM_DISTRO, "true" );
- writeConfig = true;
}
else {
appConfig.setProperty( SlipstreamConfig.STEAM_DISTRO, "false" );
- writeConfig = true;
}
}
@@ -295,7 +248,6 @@ public class FTLModManager {
if ( steamExeFile != null ) {
appConfig.setProperty( SlipstreamConfig.STEAM_EXE_PATH, steamExeFile.getAbsolutePath() );
- writeConfig = true;
log.info( "Steam located at: "+ steamExeFile.getAbsolutePath() );
}
}
@@ -308,11 +260,9 @@ public class FTLModManager {
int launchResponse = JOptionPane.showOptionDialog( null, "Would you prefer to launch FTL directly, or via Steam?", "How to Launch?", JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, launchOptions, launchOptions[1] );
if ( launchResponse == 0 ) {
appConfig.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" );
- writeConfig = true;
}
else if ( launchResponse == 1 ) {
appConfig.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "true" );
- writeConfig = true;
}
}
}
@@ -340,18 +290,6 @@ public class FTLModManager {
appConfig.setProperty( SlipstreamConfig.UPDATE_CATALOG, "0" );
appConfig.setProperty( SlipstreamConfig.UPDATE_APP, "0" );
}
- writeConfig = true;
- }
-
- if ( writeConfig ) {
- try {
- appConfig.writeConfig();
- }
- catch ( IOException e ) {
- String errorMsg = String.format( "Error writing config to \"%s\"", configFile.getPath() );
- log.error( errorMsg, e );
- showErrorDialog( errorMsg );
- }
}
ManagerFrame frame = null;
@@ -388,8 +326,6 @@ public class FTLModManager {
JOptionPane.showMessageDialog( null, message, "Error", JOptionPane.ERROR_MESSAGE );
}
-
-
private static class ExitException extends RuntimeException {
public ExitException() {
}
diff --git a/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java b/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java
index 3ad7f5b..a10f407 100644
--- a/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java
+++ b/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java
@@ -51,7 +51,6 @@ public class SlipstreamCLI {
private static Thread.UncaughtExceptionHandler exceptionHandler = null;
-
public static void main( String[] args ) {
exceptionHandler = new Thread.UncaughtExceptionHandler() {
@@ -83,77 +82,16 @@ public class SlipstreamCLI {
System.exit( 0 );
}
- DelayedDeleteHook deleteHook = new DelayedDeleteHook();
- Runtime.getRuntime().addShutdownHook( deleteHook );
-
- if ( slipstreamCmd.validate ) { // Exits (0/1).
- log.info( "Validating..." );
-
- StringBuilder resultBuf = new StringBuilder();
- ReportFormatter formatter = new ReportFormatter();
- boolean anyInvalid = false;
-
- for ( String modFileName : slipstreamCmd.modFileNames ) {
- File modFile = new File( modsDir, modFileName );
-
- if ( modFile.isDirectory() ) {
- log.info( String.format( "Zipping dir: %s/", modFile.getName() ) );
- try {
- modFile = createTempMod( modFile );
- deleteHook.addDoomedFile( modFile );
- }
- catch ( IOException e ) {
- log.error( String.format( "Error zipping dir: %s/", modFile.getName() ), e );
-
- List tmpMessages = new ArrayList();
- tmpMessages.add( new ReportMessage( ReportMessage.SECTION, modFileName ) );
- tmpMessages.add( new ReportMessage( ReportMessage.EXCEPTION, e.getMessage() ) );
-
- formatter.format( tmpMessages, resultBuf, 0 );
- resultBuf.append( "\n" );
-
- anyInvalid = true;
- continue;
- }
- }
-
- Report validateReport = ModUtilities.validateModFile( modFile );
-
- formatter.format( validateReport.messages, resultBuf, 0 );
- resultBuf.append( "\n" );
-
- if ( validateReport.outcome == false ) anyInvalid = true;
- }
- if ( resultBuf.length() == 0 ) {
- resultBuf.append( "No mods were checked." );
- }
-
- System.out.println();
- System.out.println( resultBuf.toString() );
- System.exit( anyInvalid ? 1 : 0 );
+ if ( slipstreamCmd.validate ) {
+ boolean success = validate(slipstreamCmd.modFileNames);
+ System.exit( success ? 0 : 1);
}
File configFile = new File( "modman.cfg" );
- SlipstreamConfig appConfig = getConfig( configFile );
-
- if ( slipstreamCmd.listMods ) { // Exits.
- log.info( "Listing mods..." );
-
- boolean allowZip = appConfig.getProperty( SlipstreamConfig.ALLOW_ZIP, "false" ).equals( "true" );
- File[] modFiles = modsDir.listFiles( new ModAndDirFileFilter( allowZip, true ) );
- List dirList = new ArrayList();
- List fileList = new ArrayList();
- for ( File f : modFiles ) {
- if ( f.isDirectory() )
- dirList.add( f.getName() +"/" );
- else
- fileList.add( f.getName() );
- }
- Collections.sort( dirList );
- Collections.sort( fileList );
- for ( String s : dirList ) System.out.println( s );
- for ( String s : fileList ) System.out.println( s );
+ SlipstreamConfig appConfig = new SlipstreamConfig( configFile );
+ if ( slipstreamCmd.listMods ) {
+ listMods(appConfig);
System.exit( 0 );
}
@@ -164,75 +102,97 @@ public class SlipstreamCLI {
datsDir = getDatsDir( appConfig );
}
- if ( slipstreamCmd.extractDatsDir != null ) { // Exits (0/1).
- log.info( "Extracting dats..." );
-
- File extractDir = slipstreamCmd.extractDatsDir;
-
- FolderPack dstPack = null;
- List srcPacks = new ArrayList( 2 );
- InputStream is = null;
- try {
- File ftlDatFile = new File( datsDir, "ftl.dat" );
- File dataDatFile = new File( datsDir, "data.dat" );
- File resourceDatFile = new File( datsDir, "resource.dat" );
-
- if ( ftlDatFile.exists() ) { // FTL 1.6.1.
- AbstractPack ftlPack = new PkgPack( ftlDatFile, "r" );
- srcPacks.add( ftlPack );
- }
- else if ( dataDatFile.exists() && resourceDatFile.exists() ) { // FTL 1.01-1.5.13.
- AbstractPack dataPack = new FTLPack( dataDatFile, "r" );
- AbstractPack resourcePack = new FTLPack( resourceDatFile, "r" );
- srcPacks.add( dataPack );
- srcPacks.add( resourcePack );
- }
- else {
- throw new FileNotFoundException( String.format( "Could not find either \"%s\" or both \"%s\" and \"%s\"", ftlDatFile.getName(), dataDatFile.getName(), resourceDatFile.getName() ) );
- }
-
- if ( !extractDir.exists() ) extractDir.mkdirs();
-
- dstPack = new FolderPack( extractDir );
-
- for ( AbstractPack srcPack : srcPacks ) {
- List innerPaths = srcPack.list();
-
- for ( String innerPath : innerPaths ) {
- if ( dstPack.contains( innerPath ) ) {
- log.info( "While extracting resources, this file was overwritten: "+ innerPath );
- dstPack.remove( innerPath );
- }
- is = srcPack.getInputStream( innerPath );
- dstPack.add( innerPath, is );
- }
- srcPack.close();
- }
- }
- catch ( IOException e ) {
- log.error( "Error extracting dats", e );
- System.exit( 1 );
- }
- finally {
- try {if ( is != null ) is.close();}
- catch ( IOException ex ) {}
-
- try {if ( dstPack != null ) dstPack.close();}
- catch ( IOException ex ) {}
-
- for ( AbstractPack pack : srcPacks ) {
- try {pack.close();}
- catch ( IOException ex ) {}
- }
- }
-
+ if ( slipstreamCmd.extractDatsDir != null ) {
+ extractDatsDir(slipstreamCmd, datsDir);
System.exit( 0 );
}
- if ( slipstreamCmd.patch ) { // Exits sometimes (1 on failure).
- log.info( "Patching..." );
+ if ( slipstreamCmd.patch ) {
+ boolean success = patch(slipstreamCmd, datsDir);
+ if (!success) {
+ System.exit( 1 );
+ }
+ }
- List modFiles = new ArrayList();
+ if ( slipstreamCmd.runftl ) {
+ boolean success = runFtl(appConfig, datsDir);
+ System.exit(success ? 0 : 1 );
+ }
+
+ System.exit( 0 );
+ }
+
+ private static void extractDatsDir(SlipstreamCommand slipstreamCmd, File datsDir) {
+ log.info( "Extracting dats..." );
+
+ File extractDir = slipstreamCmd.extractDatsDir;
+
+ FolderPack dstPack = null;
+ List srcPacks = new ArrayList( 2 );
+ InputStream is = null;
+ try {
+ File ftlDatFile = new File( datsDir, "ftl.dat" );
+ File dataDatFile = new File( datsDir, "data.dat" );
+ File resourceDatFile = new File( datsDir, "resource.dat" );
+
+ if ( ftlDatFile.exists() ) { // FTL 1.6.1.
+ AbstractPack ftlPack = new PkgPack( ftlDatFile, "r" );
+ srcPacks.add( ftlPack );
+ }
+ else if ( dataDatFile.exists() && resourceDatFile.exists() ) { // FTL 1.01-1.5.13.
+ AbstractPack dataPack = new FTLPack( dataDatFile, "r" );
+ AbstractPack resourcePack = new FTLPack( resourceDatFile, "r" );
+ srcPacks.add( dataPack );
+ srcPacks.add( resourcePack );
+ }
+ else {
+ throw new FileNotFoundException( String.format( "Could not find either \"%s\" or both \"%s\" and \"%s\"", ftlDatFile.getName(), dataDatFile.getName(), resourceDatFile.getName() ) );
+ }
+
+ if ( !extractDir.exists() ) extractDir.mkdirs();
+
+ dstPack = new FolderPack( extractDir );
+
+ for ( AbstractPack srcPack : srcPacks ) {
+ List innerPaths = srcPack.list();
+
+ for ( String innerPath : innerPaths ) {
+ if ( dstPack.contains( innerPath ) ) {
+ log.info( "While extracting resources, this file was overwritten: "+ innerPath );
+ dstPack.remove( innerPath );
+ }
+ is = srcPack.getInputStream( innerPath );
+ dstPack.add( innerPath, is );
+ }
+ srcPack.close();
+ }
+ }
+ catch ( IOException e ) {
+ log.error( "Error extracting dats", e );
+ System.exit( 1 );
+ }
+ finally {
+ try {if ( is != null ) is.close();}
+ catch ( IOException ex ) {}
+
+ try {if ( dstPack != null ) dstPack.close();}
+ catch ( IOException ex ) {}
+
+ for ( AbstractPack pack : srcPacks ) {
+ try {pack.close();}
+ catch ( IOException ex ) {}
+ }
+ }
+ }
+
+ private static boolean patch(SlipstreamCommand slipstreamCmd, File datsDir) {
+ log.info( "Patching..." );
+
+ DelayedDeleteHook deleteHook = new DelayedDeleteHook();
+ Runtime.getRuntime().addShutdownHook( deleteHook );
+
+ List modFiles = new ArrayList();
+ if ( slipstreamCmd.modFileNames != null ) {
for ( String modFileName : slipstreamCmd.modFileNames ) {
File modFile = new File( modsDir, modFileName );
@@ -244,130 +204,155 @@ public class SlipstreamCLI {
}
catch ( IOException e ) {
log.error( String.format( "Error zipping dir: %s/", modFile.getName() ), e );
- System.exit( 1 );
+ return false;
}
}
modFiles.add( modFile );
}
-
- boolean globalPanic = slipstreamCmd.globalPanic;
-
- SilentPatchObserver patchObserver = new SilentPatchObserver();
- ModPatchThread patchThread = new ModPatchThread( modFiles, datsDir, backupDir, globalPanic, patchObserver );
- patchThread.setDefaultUncaughtExceptionHandler( exceptionHandler );
- deleteHook.addWatchedThread( patchThread );
-
- patchThread.start();
- while ( patchThread.isAlive() ) {
- try {patchThread.join();}
- catch ( InterruptedException e ) {}
- }
-
- if ( !patchObserver.hasSucceeded() ) System.exit( 1 );
}
- if ( slipstreamCmd.runftl ) { // Exits (0/1).
- log.info( "Running FTL..." );
+ boolean globalPanic = slipstreamCmd.globalPanic;
- File exeFile = null;
- String[] exeArgs = null;
+ SilentPatchObserver patchObserver = new SilentPatchObserver();
+ ModPatchThread patchThread = new ModPatchThread( modFiles, datsDir, backupDir, globalPanic, patchObserver );
+ patchThread.setDefaultUncaughtExceptionHandler( exceptionHandler );
+ deleteHook.addWatchedThread( patchThread );
- // Try to run via Steam.
- if ( "true".equals( appConfig.getProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" ) ) ) {
+ patchThread.start();
+ while ( patchThread.isAlive() ) {
+ try {patchThread.join();}
+ catch ( InterruptedException e ) {}
+ }
- String steamPath = appConfig.getProperty( SlipstreamConfig.STEAM_EXE_PATH );
- if ( steamPath.length() > 0 ) {
- exeFile = new File( steamPath );
+ if ( !patchObserver.hasSucceeded() ) return false;
+ return true;
+ }
- if ( exeFile.exists() ) {
- exeArgs = new String[] {"-applaunch", FTLUtilities.STEAM_APPID_FTL};
- }
- else {
- log.warn( String.format( "%s does not exist: %s", SlipstreamConfig.STEAM_EXE_PATH, exeFile.getAbsolutePath() ) );
- exeFile = null;
- }
+ private static boolean validate(String[] modFileNames) {
+ DelayedDeleteHook deleteHook = new DelayedDeleteHook();
+ Runtime.getRuntime().addShutdownHook( deleteHook );
+
+ log.info( "Validating..." );
+
+ StringBuilder resultBuf = new StringBuilder();
+ ReportFormatter formatter = new ReportFormatter();
+ boolean anyInvalid = false;
+
+ for ( String modFileName : modFileNames ) {
+ File modFile = new File( modsDir, modFileName );
+
+ if ( modFile.isDirectory() ) {
+ log.info( String.format( "Zipping dir: %s/", modFile.getName() ) );
+ try {
+ modFile = createTempMod( modFile );
+ deleteHook.addDoomedFile( modFile );
}
+ catch ( IOException e ) {
+ log.error( String.format( "Error zipping dir: %s/", modFile.getName() ), e );
- if ( exeFile == null ) {
- log.warn( "Steam executable could not be found, so FTL will be launched directly" );
+ List tmpMessages = new ArrayList();
+ tmpMessages.add( new ReportMessage( ReportMessage.SECTION, modFileName ) );
+ tmpMessages.add( new ReportMessage( ReportMessage.EXCEPTION, e.getMessage() ) );
+
+ formatter.format( tmpMessages, resultBuf, 0 );
+ resultBuf.append( "\n" );
+
+ anyInvalid = true;
+ continue;
}
-
}
- // Try to run directly.
+
+ Report validateReport = ModUtilities.validateModFile( modFile );
+
+ formatter.format( validateReport.messages, resultBuf, 0 );
+ resultBuf.append( "\n" );
+
+ if ( validateReport.outcome == false ) anyInvalid = true;
+ }
+ if ( resultBuf.length() == 0 ) {
+ resultBuf.append( "No mods were checked." );
+ }
+
+ System.out.println();
+ System.out.println(resultBuf);
+ return !anyInvalid;
+ }
+
+ private static void listMods(SlipstreamConfig appConfig) {
+ log.info( "Listing mods..." );
+
+ boolean allowZip = appConfig.getProperty( SlipstreamConfig.ALLOW_ZIP, "false" ).equals( "true" );
+ File[] modFiles = modsDir.listFiles( new ModAndDirFileFilter( allowZip, true ) );
+ List dirList = new ArrayList();
+ List fileList = new ArrayList();
+ for ( File f : modFiles ) {
+ if ( f.isDirectory() )
+ dirList.add( f.getName() +"/" );
+ else
+ fileList.add( f.getName() );
+ }
+ Collections.sort( dirList );
+ Collections.sort( fileList );
+ for ( String s : dirList ) System.out.println( s );
+ for ( String s : fileList ) System.out.println( s );
+ }
+
+ private static boolean runFtl(SlipstreamConfig appConfig, File datsDir) {
+ log.info( "Running FTL..." );
+
+ File exeFile = null;
+ String[] exeArgs = null;
+
+ // Try to run via Steam.
+ if ( "true".equals( appConfig.getProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" ) ) ) {
+
+ String steamPath = appConfig.getProperty( SlipstreamConfig.STEAM_EXE_PATH );
+ if ( steamPath.length() > 0 ) {
+ exeFile = new File( steamPath );
+
+ if ( exeFile.exists() ) {
+ exeArgs = new String[] {"-applaunch", FTLUtilities.STEAM_APPID_FTL};
+ }
+ else {
+ log.warn( String.format( "%s does not exist: %s", SlipstreamConfig.STEAM_EXE_PATH, exeFile.getAbsolutePath() ) );
+ exeFile = null;
+ }
+ }
+
if ( exeFile == null ) {
- exeFile = FTLUtilities.findGameExe( datsDir );
-
- if ( exeFile != null ) {
- exeArgs = new String[0];
- } else {
- log.warn( "FTL executable could not be found" );
- }
+ log.warn( "Steam executable could not be found, so FTL will be launched directly" );
}
+ }
+ // Try to run directly.
+ if ( exeFile == null ) {
+ exeFile = FTLUtilities.findGameExe( datsDir );
+
if ( exeFile != null ) {
- try {
- FTLUtilities.launchExe( exeFile, exeArgs );
- }
- catch ( Exception e ) {
- log.error( "Error launching FTL", e );
- System.exit( 1 );
- }
+ exeArgs = new String[0];
+ } else {
+ log.warn( "FTL executable could not be found" );
}
- else {
- log.error( "No executables were found to launch FTL" );
- System.exit( 1 );
- }
-
- System.exit( 0 );
}
- System.exit( 0 );
+ if ( exeFile != null ) {
+ try {
+ FTLUtilities.launchExe( exeFile, exeArgs );
+ }
+ catch ( Exception e ) {
+ log.error( "Error launching FTL", e );
+ return false;
+ }
+ }
+ else {
+ log.error( "No executables were found to launch FTL" );
+ return false;
+ }
+
+ return true;
}
-
- /**
- * Loads settings from a config file.
- *
- * If an error occurs, it'll be logged,
- * and default settings will be returned.
- */
- private static SlipstreamConfig getConfig( File configFile ) {
-
- Properties props = new Properties();
- props.setProperty( SlipstreamConfig.ALLOW_ZIP, "false" );
- props.setProperty( SlipstreamConfig.FTL_DATS_PATH, "" );
- props.setProperty( SlipstreamConfig.STEAM_EXE_PATH, "" );
- props.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" );
- props.setProperty( SlipstreamConfig.NEVER_RUN_FTL, "false" );
- props.setProperty( SlipstreamConfig.USE_DEFAULT_UI, "false" );
- props.setProperty( SlipstreamConfig.REMEMBER_GEOMETRY, "true" );
- // "update_catalog" doesn't have a default.
- // "update_app" doesn't have a default.
- // "manager_geometry" doesn't have a default.
-
- // Read the config file.
- InputStream in = null;
- try {
- if ( configFile.exists() ) {
- log.trace( "Loading properties from config file" );
- in = new FileInputStream( configFile );
- props.load( new InputStreamReader( in, "UTF-8" ) );
- }
- }
- catch ( IOException e ) {
- log.error( "Error loading config", e );
- }
- finally {
- try {if ( in != null ) in.close();}
- catch ( IOException e ) {}
- }
-
- SlipstreamConfig appConfig = new SlipstreamConfig( props, configFile );
- return appConfig;
- }
-
-
/**
* Checks the validity of the config's dats path and returns it.
* Or exits if the path is invalid.
diff --git a/src/main/java/net/vhati/modmanager/core/SlipstreamConfig.java b/src/main/java/net/vhati/modmanager/core/SlipstreamConfig.java
index fe66234..c6fa448 100644
--- a/src/main/java/net/vhati/modmanager/core/SlipstreamConfig.java
+++ b/src/main/java/net/vhati/modmanager/core/SlipstreamConfig.java
@@ -1,18 +1,21 @@
package net.vhati.modmanager.core;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.swing.*;
+import java.io.*;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
public class SlipstreamConfig {
+ private static final Logger log = LoggerFactory.getLogger( SlipstreamConfig.class );
public static final String ALLOW_ZIP = "allow_zip";
public static final String FTL_DATS_PATH = "ftl_dats_path";
@@ -26,12 +29,35 @@ public class SlipstreamConfig {
public static final String REMEMBER_GEOMETRY = "remember_geometry";
public static final String MANAGER_GEOMETRY = "manager_geometry";
- private Properties config;
- private File configFile;
+ private final Properties config;
+ private final File configFile;
+ private final AtomicBoolean shutdownHookInitialized;
+
+ public SlipstreamConfig(File configFile) {
+ this.shutdownHookInitialized = new AtomicBoolean(false);
+
+ config = getProperties();
+
+ // Read the config file.
+ InputStream in = null;
+ try {
+ if ( configFile.exists() ) {
+ log.debug( "Loading config file" );
+ in = new FileInputStream( configFile );
+ config.load( new InputStreamReader( in, StandardCharsets.UTF_8) );
+ scheduleShutdown();
+ }
+ }
+ catch ( IOException e ) {
+ log.error( "Error loading config", e );
+ showErrorDialog( "Error loading config from "+ configFile.getPath() );
+ }
+ finally {
+ try {if ( in != null ) in.close();}
+ catch ( IOException ignored) {}
+ }
- public SlipstreamConfig( Properties config, File configFile ) {
- this.config = config;
this.configFile = configFile;
}
@@ -42,8 +68,23 @@ public class SlipstreamConfig {
this.configFile = srcConfig.getConfigFile();
this.config = new Properties();
this.config.putAll( srcConfig.getConfig() );
+ this.shutdownHookInitialized = srcConfig.shutdownHookInitialized;
}
+ private static Properties getProperties() {
+ Properties props = new Properties();
+ props.setProperty( SlipstreamConfig.ALLOW_ZIP, "false" );
+ props.setProperty( SlipstreamConfig.FTL_DATS_PATH, "" ); // Prompt.
+ props.setProperty( SlipstreamConfig.STEAM_DISTRO, "" ); // Prompt.
+ props.setProperty( SlipstreamConfig.STEAM_EXE_PATH, "" ); // Prompt.
+ props.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "" ); // Prompt.
+ props.setProperty( SlipstreamConfig.NEVER_RUN_FTL, "false" );
+ props.setProperty( SlipstreamConfig.UPDATE_CATALOG, "" ); // Prompt.
+ props.setProperty( SlipstreamConfig.UPDATE_APP, "" ); // Prompt.
+ props.setProperty( SlipstreamConfig.USE_DEFAULT_UI, "false" );
+ props.setProperty( SlipstreamConfig.REMEMBER_GEOMETRY, "true" );
+ return props;
+ }
public Properties getConfig() { return config; }
@@ -51,9 +92,30 @@ public class SlipstreamConfig {
public Object setProperty( String key, String value ) {
+ scheduleShutdown();
return config.setProperty( key, value );
}
+ private void scheduleShutdown() {
+ if (!shutdownHookInitialized.compareAndExchange(false, true)) {
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ try {
+ this.writeConfig();
+ } catch (IOException e) {
+ String errorMsg = String.format( "Error writing config to \"%s\"", configFile.getPath() );
+ log.error( errorMsg, e );
+ // TODO: only show this error when in gui mode
+ showErrorDialog( errorMsg );
+ }
+ }));
+ }
+
+ }
+
+ private static void showErrorDialog( String message ) {
+ JOptionPane.showMessageDialog( null, message, "Error", JOptionPane.ERROR_MESSAGE );
+ }
+
public int getPropertyAsInt( String key, int defaultValue ) {
String s = config.getProperty( key );
if ( s != null && s.matches("^\\d+$") )
diff --git a/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java b/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java
index 1bfcf30..742e6f7 100644
--- a/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java
+++ b/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java
@@ -1065,7 +1065,7 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
* Set this to false before an abnormal exit.
*/
public void setDisposeNormally( boolean b ) {
- disposeNormally = false;
+ disposeNormally = b;
}
@Override
diff --git a/src/main/java/net/vhati/modmanager/ui/ModPatchDialog.java b/src/main/java/net/vhati/modmanager/ui/ModPatchDialog.java
index 80a2376..bfa7932 100644
--- a/src/main/java/net/vhati/modmanager/ui/ModPatchDialog.java
+++ b/src/main/java/net/vhati/modmanager/ui/ModPatchDialog.java
@@ -13,7 +13,7 @@ public class ModPatchDialog extends ProgressDialog implements ModPatchObserver {
public ModPatchDialog( Frame owner, boolean continueOnSuccess ) {
- super( owner, true );
+ super( owner, continueOnSuccess );
this.setTitle( "Patching..." );
this.setSize( 400, 160 );
diff --git a/src/main/java/net/vhati/modmanager/ui/ModXMLSandbox.java b/src/main/java/net/vhati/modmanager/ui/ModXMLSandbox.java
index 8a07df2..401ead3 100644
--- a/src/main/java/net/vhati/modmanager/ui/ModXMLSandbox.java
+++ b/src/main/java/net/vhati/modmanager/ui/ModXMLSandbox.java
@@ -524,32 +524,33 @@ public class ModXMLSandbox extends JFrame implements ActionListener {
*/
@SuppressWarnings("unchecked")
private void buildTreeFromString( DefaultTreeModel treeModel, String path ) {
- DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode)treeModel.getRoot();
- DefaultMutableTreeNode currentNode = rootNode;
-
- String[] chunks = path.split( "/" );
-
- for ( int i=0; i < chunks.length; i++ ) {
- String chunk = chunks[i];
- if ( i < chunks.length-1 )
- chunk += "/";
-
- boolean found = false;
- Enumeration enumIt = currentNode.children();
- while ( enumIt.hasMoreElements() ) {
- DefaultMutableTreeNode tmpNode = enumIt.nextElement();
- if ( chunk.equals( tmpNode.getUserObject() ) ) {
- found = true;
- currentNode = tmpNode;
- break;
- }
- }
- if ( !found ) {
- DefaultMutableTreeNode newNode = new DefaultMutableTreeNode( chunk );
- currentNode.insert( newNode, currentNode.getChildCount() );
- currentNode = newNode;
- }
- }
+// Method commented out to get application to compile. Figure out what this did and fix it
+// DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode)treeModel.getRoot();
+// DefaultMutableTreeNode currentNode = rootNode;
+//
+// String[] chunks = path.split( "/" );
+//
+// for ( int i=0; i < chunks.length; i++ ) {
+// String chunk = chunks[i];
+// if ( i < chunks.length-1 )
+// chunk += "/";
+//
+// boolean found = false;
+// Enumeration enumIt = currentNode.children();
+// while ( enumIt.hasMoreElements() ) {
+// DefaultMutableTreeNode tmpNode = enumIt.nextElement();
+// if ( chunk.equals( tmpNode.getUserObject() ) ) {
+// found = true;
+// currentNode = tmpNode;
+// break;
+// }
+// }
+// if ( !found ) {
+// DefaultMutableTreeNode newNode = new DefaultMutableTreeNode( chunk );
+// currentNode.insert( newNode, currentNode.getChildCount() );
+// currentNode = newNode;
+// }
+// }
}