diff --git a/pom.xml b/pom.xml
index 53264db..9cbb927 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,7 +67,7 @@
info.picocli
picocli
- 2.2.0
+ 4.7.7
diff --git a/src/main/java/net/vhati/modmanager/FTLModManager.java b/src/main/java/net/vhati/modmanager/FTLModManager.java
index 3ea6dd5..edc78a1 100644
--- a/src/main/java/net/vhati/modmanager/FTLModManager.java
+++ b/src/main/java/net/vhati/modmanager/FTLModManager.java
@@ -18,15 +18,12 @@ import net.vhati.modmanager.core.ComparableVersion;
public class FTLModManager {
- private static final Logger log = LoggerFactory.getLogger( FTLModManager.class );
+ private static final Logger log = LoggerFactory.getLogger(FTLModManager.class);
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 = "TODO";
- public static final String APP_AUTHOR = "jan-leila";
+ public static final ComparableVersion APP_VERSION = new ComparableVersion("1.9.1");
-
- public static void main( String[] args ) {
+ public static void main(String[] args) {
// Redirect any libraries' java.util.Logging messages.
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
@@ -38,38 +35,29 @@ public class FTLModManager {
LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory();
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
- encoder.setContext( lc );
+ encoder.setContext(lc);
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();
- FileAppender fileAppender = new FileAppender();
- fileAppender.setContext( lc );
- fileAppender.setName( "LogFile" );
- fileAppender.setFile( new File( "./modman-log.txt" ).getAbsolutePath() );
- fileAppender.setAppend( false );
- fileAppender.setEncoder( encoder );
+ FileAppender fileAppender = new FileAppender<>();
+ fileAppender.setContext(lc);
+ fileAppender.setName("LogFile");
+ fileAppender.setFile(new File("./modman-log.txt").getAbsolutePath());
+ fileAppender.setAppend(false);
+ fileAppender.setEncoder(encoder);
fileAppender.start();
- lc.getLogger( Logger.ROOT_LOGGER_NAME ).addAppender( fileAppender );
+ lc.getLogger(Logger.ROOT_LOGGER_NAME).addAppender(fileAppender);
// Log a welcome message.
- log.debug( "Started: {}", new Date() );
- log.debug( "{} v{}", APP_NAME, APP_VERSION );
- log.debug( "OS: {} {}", System.getProperty( "os.name" ), System.getProperty( "os.version" ) );
- log.debug( "VM: {}, {}, {}", System.getProperty( "java.vm.name" ), System.getProperty( "java.version" ), System.getProperty( "os.arch" ) );
+ log.debug("Started: {}", new Date());
+ log.debug("{} v{}", APP_NAME, APP_VERSION);
+ log.debug("OS: {} {}", System.getProperty("os.name"), System.getProperty("os.version"));
+ log.debug("VM: {}, {}, {}", System.getProperty("java.vm.name"), System.getProperty("java.version"), System.getProperty("os.arch"));
- Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
- @Override
- public void uncaughtException( Thread t, Throwable e ) {
- log.error("Uncaught exception in thread: {}", t.toString(), e);
- }
- });
+ Thread.setDefaultUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception in thread: {}", t.toString(), e));
- if (args.length == 0 ) {
- System.out.println("Project called with no arguments.");
- return;
- }
- SlipstreamCLI.main( args );
+ SlipstreamCLI.main(args);
}
}
diff --git a/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java b/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java
index 358df41..330051d 100644
--- a/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java
+++ b/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java
@@ -13,11 +13,7 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import picocli.CommandLine;
-import picocli.CommandLine.Command;
-import picocli.CommandLine.IVersionProvider;
-import picocli.CommandLine.Option;
-import picocli.CommandLine.ParameterException;
-import picocli.CommandLine.Parameters;
+import picocli.CommandLine.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -39,344 +35,365 @@ import net.vhati.modmanager.core.SlipstreamConfig;
public class SlipstreamCLI {
- private static final Logger log = LoggerFactory.getLogger( SlipstreamCLI.class );
+ private static final Logger log = LoggerFactory.getLogger(SlipstreamCLI.class);
- private static final File backupDir = new File( "./backup/" );
- private static final File modsDir = new File( "./mods/" );
+ private static final File backupDir = new File("./backup/");
+ private static final File modsDir = new File("./mods/");
private static Thread.UncaughtExceptionHandler exceptionHandler = null;
- public static void main( String[] args ) {
+ public static void main(String[] args) {
exceptionHandler = (t, e) -> {
log.error("Uncaught exception in thread: {}", t.toString(), e);
- System.exit( 1 );
+ System.exit(1);
};
- SlipstreamCommand slipstreamCmd = new SlipstreamCommand();
- CommandLine commandLine = new CommandLine( slipstreamCmd );
- try {
- commandLine.parse( args );
- }
- catch ( ParameterException e ) {
- //For multiple subcommands, e.getCommandLine() returns the one that failed.
+ CommandLine commandLine = new CommandLine(new BaseCommand())
+ .addSubcommand("start", new StartCommand())
+ .addSubcommand("patch", new PatchCommand())
+ .addSubcommand("list", new ListCommand())
+ .addSubcommand("clean", new CleanCommand());
- System.err.println( "Error parsing commandline: "+ e.getMessage() );
- System.exit( 1 );
- }
+ commandLine.execute(args);
- if ( commandLine.isUsageHelpRequested() ) {
- commandLine.usage( System.out );
- System.exit( 0 );
- }
- if ( commandLine.isVersionHelpRequested() ) {
- commandLine.printVersionHelp( System.out );
- System.exit( 0 );
- }
+// try {
+// commandLine.parse(args);
+// }
+// catch (ParameterException e) {
+// //For multiple subcommands, e.getCommandLine() returns the one that failed.
+//
+// System.err.println("Error parsing commandline: "+ e.getMessage());
+// System.exit(1);
+// }
+//
+// ActionFlow actionFlow = getCommandAction(commandLine);
+//
+// switch (actionFlow) {
+// case VERSION -> {
+// commandLine.usage(System.out);
+// System.exit(0);
+// }
+// case HELP -> {
+// commandLine.printVersionHelp(System.out);
+// System.exit(0);
+// }
+// }
- if ( slipstreamCmd.validate ) {
- boolean success = validate(slipstreamCmd.modFileNames);
- System.exit( success ? 0 : 1);
- }
-
- File configFile = new File( "modman.cfg" );
- SlipstreamConfig appConfig = new SlipstreamConfig( configFile );
-
- if ( slipstreamCmd.listMods ) {
- listMods(appConfig);
- System.exit( 0 );
- }
-
- File datsDir = null;
- if ( slipstreamCmd.extractDatsDir != null ||
- slipstreamCmd.patch ||
- slipstreamCmd.runftl ) {
- datsDir = getDatsDir( appConfig );
- }
-
- 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 );
+// if (slipstreamCmd.validate) {
+// boolean success = validate(slipstreamCmd.modFileNames);
+// System.exit(success ? 0 : 1);
+// }
+//
+// File configFile = new File("modman.cfg");
+// SlipstreamConfig appConfig = new SlipstreamConfig(configFile);
+//
+// if (slipstreamCmd.listMods) {
+// listMods(appConfig);
+// System.exit(0);
+// }
+//
+// File datsDir = null;
+// if (slipstreamCmd.extractDatsDir != null ||
+// slipstreamCmd.patch ||
+// slipstreamCmd.runftl) {
+// datsDir = getDatsDir(appConfig);
+// }
+//
+// 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..." );
+// private static ActionFlow getCommandAction(CommandLine commandLine) {
+// if (commandLine.isUsageHelpRequested()) {
+// return ActionFlow.HELP;
+// }
+// if (commandLine.isVersionHelpRequested()) {
+// return ActionFlow.VERSION;
+// }
+//
+// return null;
+// }
- File extractDir = slipstreamCmd.extractDatsDir;
+// private static void extractDatsDir(RunCommand 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()) {
+// boolean success = extractDir.mkdirs();
+// if (success) {
+// log.error("Error extracting dats");
+// System.exit(1);
+// }
+// };
+//
+// 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 ignored) {}
+//
+// try {if (dstPack != null) dstPack.close();}
+// catch (IOException ignored) {}
+//
+// for (AbstractPack pack : srcPacks) {
+// try {pack.close();}
+// catch (IOException ignored) {}
+// }
+// }
+// }
+//
+// private static boolean patch(RunCommand 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);
+//
+// if (modFile.isDirectory()) {
+// log.info("Zipping dir: {}/", modFile.getName());
+// try {
+// modFile = createTempMod(modFile);
+// deleteHook.addDoomedFile(modFile);
+// }
+// catch (IOException e) {
+// log.error("Error zipping dir: {}/", modFile.getName(), e);
+// return false;
+// }
+// }
+//
+// modFiles.add(modFile);
+// }
+// }
+//
+// boolean globalPanic = slipstreamCmd.globalPanic;
+//
+// SilentPatchObserver patchObserver = new SilentPatchObserver();
+// ModPatchThread patchThread = new ModPatchThread(modFiles, datsDir, backupDir, globalPanic, patchObserver);
+// Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
+// deleteHook.addWatchedThread(patchThread);
+//
+// patchThread.start();
+// while (patchThread.isAlive()) {
+// try {patchThread.join();}
+// catch (InterruptedException ignored) {}
+// }
+//
+// return patchObserver.hasSucceeded();
+// }
- 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" );
+// 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("Zipping dir: {}/", modFile.getName());
+// try {
+// modFile = createTempMod(modFile);
+// deleteHook.addDoomedFile(modFile);
+// }
+// catch (IOException e) {
+// log.error("Error zipping dir: {}/", 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) anyInvalid = true;
+// }
+// if (resultBuf.isEmpty()) {
+// 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<>();
+// assert modFiles != null;
+// 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.isEmpty()) {
+// 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) {
+// 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) {
+// exeArgs = new String[0];
+// } else {
+// log.warn("FTL executable could not be found");
+// }
+// }
+//
+// 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;
+// }
- 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() ) {
- boolean success = extractDir.mkdirs();
- if (success) {
- log.error( "Error extracting dats");
- System.exit( 1 );
- }
- };
-
- 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 ignored) {}
-
- try {if ( dstPack != null ) dstPack.close();}
- catch ( IOException ignored) {}
-
- for ( AbstractPack pack : srcPacks ) {
- try {pack.close();}
- catch ( IOException ignored) {}
- }
- }
- }
-
- 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 );
-
- if ( modFile.isDirectory() ) {
- log.info("Zipping dir: {}/", modFile.getName());
- try {
- modFile = createTempMod( modFile );
- deleteHook.addDoomedFile( modFile );
- }
- catch ( IOException e ) {
- log.error("Error zipping dir: {}/", modFile.getName(), e);
- return false;
- }
- }
-
- modFiles.add( modFile );
- }
- }
-
- boolean globalPanic = slipstreamCmd.globalPanic;
-
- SilentPatchObserver patchObserver = new SilentPatchObserver();
- ModPatchThread patchThread = new ModPatchThread( modFiles, datsDir, backupDir, globalPanic, patchObserver );
- Thread.setDefaultUncaughtExceptionHandler( exceptionHandler );
- deleteHook.addWatchedThread( patchThread );
-
- patchThread.start();
- while ( patchThread.isAlive() ) {
- try {patchThread.join();}
- catch ( InterruptedException ignored) {}
- }
-
- return patchObserver.hasSucceeded();
- }
-
- 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("Zipping dir: {}/", modFile.getName());
- try {
- modFile = createTempMod( modFile );
- deleteHook.addDoomedFile( modFile );
- }
- catch ( IOException e ) {
- log.error("Error zipping dir: {}/", 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) anyInvalid = true;
- }
- if (resultBuf.isEmpty()) {
- 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<>();
- assert modFiles != null;
- 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.isEmpty()) {
- 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 ) {
- 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 ) {
- exeArgs = new String[0];
- } else {
- log.warn( "FTL executable could not be found" );
- }
- }
-
- 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;
- }
-
- /**
- * Checks the validity of the config's dats path and returns it.
- * Or exits if the path is invalid.
- */
- private static File getDatsDir( SlipstreamConfig appConfig ) {
- File datsDir = null;
- String datsPath = appConfig.getProperty( SlipstreamConfig.FTL_DATS_PATH, "" );
-
- if (!datsPath.isEmpty()) {
- log.info( "Using FTL dats path from config: "+ datsPath );
- datsDir = new File( datsPath );
- if (!FTLUtilities.isDatsDirValid(datsDir)) {
- log.error( "The config's "+ SlipstreamConfig.FTL_DATS_PATH +" does not exist, or it is invalid" );
- datsDir = null;
- }
- }
- else {
- log.error( "No FTL dats path previously set" );
- }
- if ( datsDir == null ) {
- log.error( "Run the GUI once, or edit the config file, and try again" );
- System.exit( 1 );
- }
-
- return datsDir;
- }
+// /**
+// * Checks the validity of the config's dats path and returns it.
+// * Or exits if the path is invalid.
+// */
+// private static File getDatsDir(SlipstreamConfig appConfig) {
+// File datsDir = null;
+// String datsPath = appConfig.getProperty(SlipstreamConfig.FTL_DATS_PATH, "");
+//
+// if (!datsPath.isEmpty()) {
+// log.info("Using FTL dats path from config: "+ datsPath);
+// datsDir = new File(datsPath);
+// if (!FTLUtilities.isDatsDirValid(datsDir)) {
+// log.error("The config's "+ SlipstreamConfig.FTL_DATS_PATH +" does not exist, or it is invalid");
+// datsDir = null;
+// }
+// }
+// else {
+// log.error("No FTL dats path previously set");
+// }
+// if (datsDir == null) {
+// log.error("Run the GUI once, or edit the config file, and try again");
+// System.exit(1);
+// }
+//
+// return datsDir;
+// }
/**
@@ -384,8 +401,8 @@ public class SlipstreamCLI {
* Empty subdirs will be omitted.
* The archive will be not be deleted on exit (handle that elsewhere).
*/
- private static File createTempMod( File dir ) throws IOException {
- File tempFile = File.createTempFile( dir.getName() +"_temp-", ".zip" );
+ private static File createTempMod(File dir) throws IOException {
+ File tempFile = File.createTempFile(dir.getName() +"_temp-", ".zip");
try (FileOutputStream fos = new FileOutputStream(tempFile)) {
ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(fos));
@@ -397,12 +414,12 @@ public class SlipstreamCLI {
return tempFile;
}
- private static void addDirToArchive( ZipOutputStream zos, File dir, String pathPrefix ) throws IOException {
- if ( pathPrefix == null ) pathPrefix = "";
+ private static void addDirToArchive(ZipOutputStream zos, File dir, String pathPrefix) throws IOException {
+ if (pathPrefix == null) pathPrefix = "";
- for ( File f : Objects.requireNonNull(dir.listFiles())) {
- if ( f.isDirectory() ) {
- addDirToArchive( zos, f, pathPrefix + f.getName() +"/" );
+ for (File f : Objects.requireNonNull(dir.listFiles())) {
+ if (f.isDirectory()) {
+ addDirToArchive(zos, f, pathPrefix + f.getName() +"/");
continue;
}
@@ -421,50 +438,228 @@ public class SlipstreamCLI {
}
}
-
+ @Command(
+ name = "list",
+ description = "list all available mods"
+ )
+ public static class ListCommand implements Runnable {
+ @Override
+ public void run() {
+ System.out.println("list command");
+ }
+ }
@Command(
- name = "modman",
- abbreviateSynopsis = true,
- sortOptions = false,
- description = "Perform actions against an FTL installation and/or a list of named mods.",
- footer = "%nIf a named mod is a directory, a temporary zip will be created.",
- versionProvider = SlipstreamVersionProvider.class
+ name = "patch",
+ description = "create a patched binary using the available mods"
)
- public static class SlipstreamCommand {
- @Option(names = "--extract-dats", paramLabel = "DIR", description = "extract FTL resources into a dir")
- File extractDatsDir;
+ public static class PatchCommand implements Runnable {
+ @Parameters(index = "0", description = "the location unpatched binary is located at")
+ File binary;
- @Option(names = "--global-panic", description = "patch as if advanced find tags had panic='true'")
- boolean globalPanic;
+ @Parameters(description = "list of mods that the binary will be patched with before it starts. (defaults to all mods if non are provided)", mapFallbackValue = Option.NULL_VALUE)
+ String mods;
- @Option(names = "--list-mods", description = "list available mod names")
- boolean listMods;
+ @Option(names = "--dry", description = "skip the patching step but still run the rest of the application")
+ boolean dry;
- @Option(names = "--runftl", description = "run the game (standalone or with 'patch')")
- boolean runftl;
+ @Override
+ public void run() {
+ System.out.println("patch command");
+ }
+ }
- @Option(names = "--patch", description = "revert to vanilla and add named mods (if any)")
- boolean patch;
+ @Command(
+ name = "clean",
+ description = "remove all patched binaries"
+ )
+ public static class CleanCommand implements Runnable {
+ @Override
+ public void run() {
+ System.out.println("clean command");
+ }
+ }
- @Option(names = "--validate", description = "check named mods for problems")
- boolean validate;
+ @Command(
+ name = "install",
+ description = "install the slipstream mod manager"
+ )
+ public static class InstallCommand implements Runnable {
+ @Override
+ public void run() {
+ System.out.println("install command");
+ }
+ }
- @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help and exit")
- boolean helpRequested;
+ @Command(
+ name = "uninstall",
+ description = "uninstall the slipstream mod manager"
+ )
+ public static class UninstallCommand implements Runnable {
+ @Override
+ public void run() {
+ System.out.println("uninstall command");
+ }
+ }
+ @Command(
+ name = "start",
+ abbreviateSynopsis = true,
+ sortOptions = false,
+ description = "Creates and starts a patched version of FTL.",
+ versionProvider = SlipstreamVersionProvider.class
+ )
+ public static class StartCommand implements Runnable {
@Option(names = "--version", versionHelp = true, description = "output version information and exit")
- boolean versionRequested;
+ boolean isVersionCommand;
+
+ @Option(names = { "--game-folder", "--binary", "--data-folder" }, description = "the location of the game files", converter = GameDirectoryConverter.class, mapFallbackValue = Option.NULL_VALUE)
+ GameDirectory gameDirectory;
+
+ @Parameters(description = "list of mods that the binary will be patched with before it starts. (defaults to all mods if non are provided)", mapFallbackValue = Option.NULL_VALUE)
+ String[] mods;
+
+ @Option(names = "--dry", description = "skip the patching step but still run the rest of the application")
+ boolean dry;
+
+ @Override
+ public void run() {
+ System.out.println("start command: " + gameDirectory.root.getAbsolutePath());
+ }
+ }
+
+ static class GameDirectoryConverter implements ITypeConverter {
+
+ private GameDirectory getGameDirectory(String fileName) {
+ if (fileName == null ) {
+ // TODO: get this from env variables
+ // TODO: if env variable not set check some common places and prompt the user if they want to use that
+ return null;
+ }
+ return GameDirectory.createGameDirectory(new File(fileName));
+ }
+
+ @Override
+ public GameDirectory convert(String fileName) {
+ GameDirectory gameDirectory = getGameDirectory(fileName);
+ if (gameDirectory == null) {
+ System.out.println("Game not found. Please specify the --game-folder argument, or set the FTL_GAME_FOLDER environment variable.");
+ System.exit(1);
+ }
+ return gameDirectory;
+ }
+ }
+
+ static class GameDirectory {
+
+ private static final String GAME_FOLDER_NAME = "FTL Faster Than Light";
+ private static final String GAME_DATA_FOLDER_NAME = "data";
+ private static final String MODS_FOLDER_NAME = "mods";
+ private static final String BACKUP_FOLDER_NAME = "backup";
+ private static final String LAUNCH_SCRIPT_FILE_NAME = "FTL";
+ private static final String X86_BINARY_FILE_NAME = "FTL.x86";
+
+ private static boolean isValidGameDirectory(File gameDirectory) {
+ if (
+ !(
+ gameDirectory.exists()
+ && gameDirectory.isDirectory()
+ && gameDirectory.getName().equals(GAME_FOLDER_NAME)
+ )
+ ) {
+ return false;
+ }
+
+ File gameDataFolder = new File(gameDirectory, GAME_DATA_FOLDER_NAME);
+ if (
+ !(
+ gameDataFolder.exists()
+ && gameDataFolder.isDirectory()
+ )
+ ) {
+ return false;
+ }
+
+ File gameLaunchScript = new File(gameDataFolder, LAUNCH_SCRIPT_FILE_NAME);
+ if (
+ !(
+ gameLaunchScript.exists()
+ && gameLaunchScript.isFile()
+ )
+ ) {
+ return false;
+ }
+
+ // TODO: deal with FTL.amd64
+ File gameBinary = new File(gameDataFolder, X86_BINARY_FILE_NAME);
+ return gameBinary.exists() && gameBinary.isFile();
+ }
+
+ static GameDirectory createGameDirectory(File file) {
+ if (file.isFile()) {
+ String filename = file.getName();
+ if (filename.equals(X86_BINARY_FILE_NAME)) {
+ File root = file.getParentFile().getParentFile();
+ if (isValidGameDirectory(root)) {
+ return new GameDirectory((root));
+ }
+ }
+ }
+ if (file.isDirectory()){
+ if (isValidGameDirectory(file)) {
+ return new GameDirectory(file);
+ }
+
+ String directoryName = file.getName();
+ if (directoryName.equals(GAME_DATA_FOLDER_NAME)) {
+ File root = file.getParentFile();
+ if (isValidGameDirectory(root)) {
+ return new GameDirectory((root));
+ }
+ }
+ }
+ return null;
+ }
+
+ public final File root;
+ public final File dataDir;
+ public final File modsDir;
+ public final File backupDir;
+ public final File launchScript;
+ public final File binary;
+
+ GameDirectory(File root) {
+ this.root = root;
+ this.dataDir = new File(root, "data");
+ this.modsDir = new File(this.dataDir, "mods");
+ this.backupDir = new File(this.dataDir, "backup");
+ this.launchScript = new File(this.dataDir, "FTL");
+ // TODO: this should probably change when we need to use FTL.amd64 instead
+ this.binary = new File(this.dataDir, X86_BINARY_FILE_NAME);
+ }
+ }
+
+ @Command(name = "slipstream")
+ public static class BaseCommand implements Runnable {
+ @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help and exit")
+ boolean isHelpCommand;
+
+ @Spec
+ Model.CommandSpec spec;
+
+ @Override
+ public void run() {
+ // if the command was invoked without subcommand, show the usage help
+ spec.commandLine().usage(System.err);
+ }
- @Parameters(paramLabel = "MODFILE", description = "names of files or directories in the mods/ dir")
- String[] modFileNames;
}
public static class SlipstreamVersionProvider implements IVersionProvider {
@Override
public String[] getVersion() {
return new String[] {
- String.format( "%s %s", FTLModManager.APP_NAME, FTLModManager.APP_VERSION ),
+ String.format("%s %s", FTLModManager.APP_NAME, FTLModManager.APP_VERSION),
"Copyright (C) 2013,2014,2017,2018 David Millis",
"",
"This program is free software; you can redistribute it and/or modify",
@@ -490,19 +685,19 @@ public class SlipstreamCLI {
private boolean succeeded = false;
@Override
- public void patchingProgress( final int value, final int max ) {
+ public void patchingProgress(final int value, final int max) {
}
@Override
- public void patchingStatus( String message ) {
+ public void patchingStatus(String message) {
}
@Override
- public void patchingMod( File modFile ) {
+ public void patchingMod(File modFile) {
}
@Override
- public synchronized void patchingEnded( boolean outcome, Exception e ) {
+ public synchronized void patchingEnded(boolean outcome, Exception e) {
succeeded = outcome;
done = true;
}
@@ -512,26 +707,18 @@ public class SlipstreamCLI {
}
-
- private static class ModAndDirFileFilter implements FileFilter {
- private final boolean allowZip;
- private final boolean allowDirs;
-
- public ModAndDirFileFilter( boolean allowZip, boolean allowDirs ) {
- this.allowZip = allowZip;
- this.allowDirs = allowDirs;
- }
+ private record ModAndDirFileFilter(boolean allowZip, boolean allowDirs) implements FileFilter {
@Override
- public boolean accept( File f ) {
- if ( f.isDirectory() ) return allowDirs;
+ public boolean accept(File f) {
+ if (f.isDirectory()) return allowDirs;
- if ( f.getName().endsWith(".ftl") ) return true;
+ if (f.getName().endsWith(".ftl")) return true;
- if ( allowZip ) {
- return f.getName().endsWith(".zip");
+ if (allowZip) {
+ return f.getName().endsWith(".zip");
+ }
+ return false;
}
- return false;
}
- }
}