Compare commits

...

11 commits

Author SHA1 Message Date
ccd50b0275 simplified loading of SlipstreamConfig 2025-08-16 22:17:07 -05:00
9e8e830ab1 refactored huge method in SlipstreamCLI 2025-08-16 20:37:09 -05:00
9861d7242e made application not fail to compile 2025-08-16 20:36:53 -05:00
8b344d2c53 added nix dev shell 2025-08-16 13:36:43 -05:00
c0795e234a
replaced bash shebang with more portable one 2025-08-16 11:12:06 -05:00
Vhati
85cad4ffbe
Merge pull request #16 from kartoFlane/patch-2
Fixed ModPatchDialog constructor ignoring continueOnSuccess argument
2018-03-08 17:23:42 -05:00
Tomasz Bachmiński
dcec887f17
Update ModPatchDialog.java
Fixed ModPatchDialog constructor ignoring continueOnSuccess argument
2018-03-08 00:20:51 +01:00
Vhati
4bb3a3ddb9
Merge pull request #14 from SagePtr/patch-1
Fixed an exception when using CLI's --patch arg without any mods
2018-03-06 21:45:07 -05:00
Vhati
23dbdc8ab2
Merge pull request #15 from kartoFlane/patch-1
Fixed setDisposeNormally() ignoring the argument
2018-03-06 21:42:03 -05:00
Tomasz Bachmiński
d89e53656e
Fixed setDisposeNormally() ignoring the argument
Calling ManagerFrame.setDisposeNormally() would always set the `disposeNormally` variable to false, regardless of the argument value.
2018-03-06 23:32:26 +01:00
SagePtr
871cbde44a
Update SlipstreamCLI.java
Fixed --patch without parameters (NullPointerException)
2018-03-05 19:32:54 +02:00
14 changed files with 447 additions and 353 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

7
.gitignore vendored
View file

@ -5,18 +5,19 @@ target/
*.bak *.bak
~$* ~$*
# Windows image file caches # Windows image file caches
Thumbs.db Thumbs.db
# Windows folder config file # Windows folder config file
Desktop.ini Desktop.ini
# Mac junk # Mac junk
.DS_Store .DS_Store
# IdeaJ # IdeaJ
*.idea *.idea
*.iml *.iml
# enviroemnt packages
.direnv

25
flake.lock generated Normal file
View file

@ -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
}

69
flake.nix Normal file
View file

@ -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}"
'';
};
}
);
};
}

View file

@ -11,8 +11,8 @@
</prerequisites> </prerequisites>
<properties> <properties>
<jdk.version>1.6</jdk.version> <jdk.version>1.6</jdk.version>
<maven.compiler.source>1.6</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target> <maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>

14
shell.nix Normal file
View file

@ -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

View file

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# Get the absolute path to this script's folder. # Get the absolute path to this script's folder.
if echo "$0" | awk '{exit(!/^\//);}'; then if echo "$0" | awk '{exit(!/^\//);}'; then

View file

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# Get the script's name. # Get the script's name.
me=$(basename "$0"); me=$(basename "$0");

View file

@ -1,19 +1,14 @@
package net.vhati.modmanager; package net.vhati.modmanager;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.StandardCharsets;
import java.util.Date; import java.util.Date;
import java.util.Properties;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent; 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 String APP_NAME = "Slipstream Mod Manager";
public static final ComparableVersion APP_VERSION = new ComparableVersion( "1.9.1" ); 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_URL = "TODO";
public static final String APP_AUTHOR = "Vhati"; public static final String APP_AUTHOR = "jan-leila";
public static void main( String[] args ) { public static void main( String[] args ) {
@ -53,7 +48,7 @@ public class FTLModManager {
PatternLayoutEncoder encoder = new PatternLayoutEncoder(); PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setContext( lc ); 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.setPattern( "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" );
encoder.start(); encoder.start();
@ -76,7 +71,7 @@ public class FTLModManager {
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override @Override
public void uncaughtException( Thread t, Throwable e ) { 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. // Ensure all popups are triggered from the event dispatch thread.
SwingUtilities.invokeLater(FTLModManager::guiInit);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
guiInit();
}
});
} }
private static void guiInit() { private static void guiInit() {
try { try {
// TODO: get mods file from env var
// Nag if the jar was double-clicked. // 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(); String currentPath = new File( "." ).getAbsoluteFile().getParentFile().getAbsolutePath();
log.error( String.format( "Slipstream could not find its own folder (Currently in \"%s\"), exiting...", currentPath ) ); 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(); throw new ExitException();
} }
// TODO: get config file from env var
File configFile = new File( "modman.cfg" ); File configFile = new File( "modman.cfg" );
boolean writeConfig = false; SlipstreamConfig appConfig = new SlipstreamConfig(configFile);
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 );
// Look-and-Feel. // 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 ) { if ( !useDefaultUI ) {
LookAndFeel defaultLaf = UIManager.getLookAndFeel(); LookAndFeel defaultLaf = UIManager.getLookAndFeel();
@ -157,7 +114,7 @@ public class FTLModManager {
log.debug( "Setting system look and feel: "+ UIManager.getSystemLookAndFeelClassName() ); log.debug( "Setting system look and feel: "+ UIManager.getSystemLookAndFeelClassName() );
// SystemLaf is risky. It may throw an exception, or lead to graphical bugs. // 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() ); UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
} }
catch ( Exception e ) { 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..." ); log.info( "Setting "+ SlipstreamConfig.USE_DEFAULT_UI +"=true in the config file to prevent this error..." );
appConfig.setProperty( SlipstreamConfig.USE_DEFAULT_UI, "true" ); appConfig.setProperty( SlipstreamConfig.USE_DEFAULT_UI, "true" );
writeConfig = true;
try { try {
UIManager.setLookAndFeel( defaultLaf ); UIManager.setLookAndFeel( defaultLaf );
@ -220,7 +176,6 @@ public class FTLModManager {
if ( datsDir != null ) { if ( datsDir != null ) {
appConfig.setProperty( SlipstreamConfig.FTL_DATS_PATH, datsDir.getAbsolutePath() ); appConfig.setProperty( SlipstreamConfig.FTL_DATS_PATH, datsDir.getAbsolutePath() );
writeConfig = true;
log.info( "FTL dats located at: "+ datsDir.getAbsolutePath() ); 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 ); int steamBasedResponse = JOptionPane.showConfirmDialog( null, "Was FTL installed via Steam?", "Confirm", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE );
if ( steamBasedResponse == JOptionPane.YES_OPTION ) { if ( steamBasedResponse == JOptionPane.YES_OPTION ) {
appConfig.setProperty( SlipstreamConfig.STEAM_DISTRO, "true" ); appConfig.setProperty( SlipstreamConfig.STEAM_DISTRO, "true" );
writeConfig = true;
} }
else { else {
appConfig.setProperty( SlipstreamConfig.STEAM_DISTRO, "false" ); appConfig.setProperty( SlipstreamConfig.STEAM_DISTRO, "false" );
writeConfig = true;
} }
} }
@ -295,7 +248,6 @@ public class FTLModManager {
if ( steamExeFile != null ) { if ( steamExeFile != null ) {
appConfig.setProperty( SlipstreamConfig.STEAM_EXE_PATH, steamExeFile.getAbsolutePath() ); appConfig.setProperty( SlipstreamConfig.STEAM_EXE_PATH, steamExeFile.getAbsolutePath() );
writeConfig = true;
log.info( "Steam located at: "+ steamExeFile.getAbsolutePath() ); 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] ); 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 ) { if ( launchResponse == 0 ) {
appConfig.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" ); appConfig.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" );
writeConfig = true;
} }
else if ( launchResponse == 1 ) { else if ( launchResponse == 1 ) {
appConfig.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "true" ); 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_CATALOG, "0" );
appConfig.setProperty( SlipstreamConfig.UPDATE_APP, "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; ManagerFrame frame = null;
@ -388,8 +326,6 @@ public class FTLModManager {
JOptionPane.showMessageDialog( null, message, "Error", JOptionPane.ERROR_MESSAGE ); JOptionPane.showMessageDialog( null, message, "Error", JOptionPane.ERROR_MESSAGE );
} }
private static class ExitException extends RuntimeException { private static class ExitException extends RuntimeException {
public ExitException() { public ExitException() {
} }

View file

@ -51,7 +51,6 @@ public class SlipstreamCLI {
private static Thread.UncaughtExceptionHandler exceptionHandler = null; private static Thread.UncaughtExceptionHandler exceptionHandler = null;
public static void main( String[] args ) { public static void main( String[] args ) {
exceptionHandler = new Thread.UncaughtExceptionHandler() { exceptionHandler = new Thread.UncaughtExceptionHandler() {
@ -83,77 +82,16 @@ public class SlipstreamCLI {
System.exit( 0 ); System.exit( 0 );
} }
DelayedDeleteHook deleteHook = new DelayedDeleteHook(); if ( slipstreamCmd.validate ) {
Runtime.getRuntime().addShutdownHook( deleteHook ); boolean success = validate(slipstreamCmd.modFileNames);
System.exit( success ? 0 : 1);
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<ReportMessage> tmpMessages = new ArrayList<ReportMessage>();
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 );
} }
File configFile = new File( "modman.cfg" ); File configFile = new File( "modman.cfg" );
SlipstreamConfig appConfig = getConfig( configFile ); SlipstreamConfig appConfig = new SlipstreamConfig( 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<String> dirList = new ArrayList<String>();
List<String> fileList = new ArrayList<String>();
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 );
if ( slipstreamCmd.listMods ) {
listMods(appConfig);
System.exit( 0 ); System.exit( 0 );
} }
@ -164,7 +102,27 @@ public class SlipstreamCLI {
datsDir = getDatsDir( appConfig ); datsDir = getDatsDir( appConfig );
} }
if ( slipstreamCmd.extractDatsDir != null ) { // Exits (0/1). if ( slipstreamCmd.extractDatsDir != null ) {
extractDatsDir(slipstreamCmd, datsDir);
System.exit( 0 );
}
if ( slipstreamCmd.patch ) {
boolean success = patch(slipstreamCmd, datsDir);
if (!success) {
System.exit( 1 );
}
}
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..." ); log.info( "Extracting dats..." );
File extractDir = slipstreamCmd.extractDatsDir; File extractDir = slipstreamCmd.extractDatsDir;
@ -225,14 +183,16 @@ public class SlipstreamCLI {
catch ( IOException ex ) {} catch ( IOException ex ) {}
} }
} }
System.exit( 0 );
} }
if ( slipstreamCmd.patch ) { // Exits sometimes (1 on failure). private static boolean patch(SlipstreamCommand slipstreamCmd, File datsDir) {
log.info( "Patching..." ); log.info( "Patching..." );
DelayedDeleteHook deleteHook = new DelayedDeleteHook();
Runtime.getRuntime().addShutdownHook( deleteHook );
List<File> modFiles = new ArrayList<File>(); List<File> modFiles = new ArrayList<File>();
if ( slipstreamCmd.modFileNames != null ) {
for ( String modFileName : slipstreamCmd.modFileNames ) { for ( String modFileName : slipstreamCmd.modFileNames ) {
File modFile = new File( modsDir, modFileName ); File modFile = new File( modsDir, modFileName );
@ -244,12 +204,13 @@ public class SlipstreamCLI {
} }
catch ( IOException e ) { catch ( IOException e ) {
log.error( String.format( "Error zipping dir: %s/", modFile.getName() ), e ); log.error( String.format( "Error zipping dir: %s/", modFile.getName() ), e );
System.exit( 1 ); return false;
} }
} }
modFiles.add( modFile ); modFiles.add( modFile );
} }
}
boolean globalPanic = slipstreamCmd.globalPanic; boolean globalPanic = slipstreamCmd.globalPanic;
@ -264,10 +225,80 @@ public class SlipstreamCLI {
catch ( InterruptedException e ) {} catch ( InterruptedException e ) {}
} }
if ( !patchObserver.hasSucceeded() ) System.exit( 1 ); if ( !patchObserver.hasSucceeded() ) return false;
return true;
} }
if ( slipstreamCmd.runftl ) { // Exits (0/1). 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 );
List<ReportMessage> tmpMessages = new ArrayList<ReportMessage>();
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);
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<String> dirList = new ArrayList<String>();
List<String> fileList = new ArrayList<String>();
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..." ); log.info( "Running FTL..." );
File exeFile = null; File exeFile = null;
@ -311,63 +342,17 @@ public class SlipstreamCLI {
} }
catch ( Exception e ) { catch ( Exception e ) {
log.error( "Error launching FTL", e ); log.error( "Error launching FTL", e );
System.exit( 1 ); return false;
} }
} }
else { else {
log.error( "No executables were found to launch FTL" ); log.error( "No executables were found to launch FTL" );
System.exit( 1 ); return false;
} }
System.exit( 0 ); return true;
} }
System.exit( 0 );
}
/**
* 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. * Checks the validity of the config's dats path and returns it.
* Or exits if the path is invalid. * Or exits if the path is invalid.

View file

@ -1,18 +1,21 @@
package net.vhati.modmanager.core; package net.vhati.modmanager.core;
import java.io.File; import org.slf4j.Logger;
import java.io.FileOutputStream; import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.OutputStream; import javax.swing.*;
import java.io.OutputStreamWriter; import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
public class SlipstreamConfig { public class SlipstreamConfig {
private static final Logger log = LoggerFactory.getLogger( SlipstreamConfig.class );
public static final String ALLOW_ZIP = "allow_zip"; public static final String ALLOW_ZIP = "allow_zip";
public static final String FTL_DATS_PATH = "ftl_dats_path"; 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 REMEMBER_GEOMETRY = "remember_geometry";
public static final String MANAGER_GEOMETRY = "manager_geometry"; public static final String MANAGER_GEOMETRY = "manager_geometry";
private Properties config; private final Properties config;
private File configFile; 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; this.configFile = configFile;
} }
@ -42,8 +68,23 @@ public class SlipstreamConfig {
this.configFile = srcConfig.getConfigFile(); this.configFile = srcConfig.getConfigFile();
this.config = new Properties(); this.config = new Properties();
this.config.putAll( srcConfig.getConfig() ); 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; } public Properties getConfig() { return config; }
@ -51,9 +92,30 @@ public class SlipstreamConfig {
public Object setProperty( String key, String value ) { public Object setProperty( String key, String value ) {
scheduleShutdown();
return config.setProperty( key, value ); 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 ) { public int getPropertyAsInt( String key, int defaultValue ) {
String s = config.getProperty( key ); String s = config.getProperty( key );
if ( s != null && s.matches("^\\d+$") ) if ( s != null && s.matches("^\\d+$") )

View file

@ -1065,7 +1065,7 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
* Set this to false before an abnormal exit. * Set this to false before an abnormal exit.
*/ */
public void setDisposeNormally( boolean b ) { public void setDisposeNormally( boolean b ) {
disposeNormally = false; disposeNormally = b;
} }
@Override @Override

View file

@ -13,7 +13,7 @@ public class ModPatchDialog extends ProgressDialog implements ModPatchObserver {
public ModPatchDialog( Frame owner, boolean continueOnSuccess ) { public ModPatchDialog( Frame owner, boolean continueOnSuccess ) {
super( owner, true ); super( owner, continueOnSuccess );
this.setTitle( "Patching..." ); this.setTitle( "Patching..." );
this.setSize( 400, 160 ); this.setSize( 400, 160 );

View file

@ -524,32 +524,33 @@ public class ModXMLSandbox extends JFrame implements ActionListener {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void buildTreeFromString( DefaultTreeModel treeModel, String path ) { private void buildTreeFromString( DefaultTreeModel treeModel, String path ) {
DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode)treeModel.getRoot(); // Method commented out to get application to compile. Figure out what this did and fix it
DefaultMutableTreeNode currentNode = rootNode; // DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode)treeModel.getRoot();
// DefaultMutableTreeNode currentNode = rootNode;
String[] chunks = path.split( "/" ); //
// String[] chunks = path.split( "/" );
for ( int i=0; i < chunks.length; i++ ) { //
String chunk = chunks[i]; // for ( int i=0; i < chunks.length; i++ ) {
if ( i < chunks.length-1 ) // String chunk = chunks[i];
chunk += "/"; // if ( i < chunks.length-1 )
// chunk += "/";
boolean found = false; //
Enumeration<DefaultMutableTreeNode> enumIt = currentNode.children(); // boolean found = false;
while ( enumIt.hasMoreElements() ) { // Enumeration<DefaultMutableTreeNode> enumIt = currentNode.children();
DefaultMutableTreeNode tmpNode = enumIt.nextElement(); // while ( enumIt.hasMoreElements() ) {
if ( chunk.equals( tmpNode.getUserObject() ) ) { // DefaultMutableTreeNode tmpNode = enumIt.nextElement();
found = true; // if ( chunk.equals( tmpNode.getUserObject() ) ) {
currentNode = tmpNode; // found = true;
break; // currentNode = tmpNode;
} // break;
} // }
if ( !found ) { // }
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode( chunk ); // if ( !found ) {
currentNode.insert( newNode, currentNode.getChildCount() ); // DefaultMutableTreeNode newNode = new DefaultMutableTreeNode( chunk );
currentNode = newNode; // currentNode.insert( newNode, currentNode.getChildCount() );
} // currentNode = newNode;
} // }
// }
} }