Added commandline interface
This commit is contained in:
parent
4a091297ed
commit
a585047427
13 changed files with 731 additions and 149 deletions
5
pom.xml
5
pom.xml
|
@ -46,6 +46,11 @@
|
||||||
<artifactId>jdom2</artifactId>
|
<artifactId>jdom2</artifactId>
|
||||||
<version>2.0.5</version>
|
<version>2.0.5</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-cli</groupId>
|
||||||
|
<artifactId>commons-cli</artifactId>
|
||||||
|
<version>1.2</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -39,6 +39,10 @@ This project depends on the following libraries.
|
||||||
http://logging.apache.org/log4j/2.x/
|
http://logging.apache.org/log4j/2.x/
|
||||||
(JavaDocs are not available.)
|
(JavaDocs are not available.)
|
||||||
|
|
||||||
|
- Apache Commons CLI 1.2
|
||||||
|
http://commons.apache.org/proper/commons-cli/
|
||||||
|
(For JavaDocs, scroll down.)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Here's a batch file that builds when double-clicked (edit the vars).
|
Here's a batch file that builds when double-clicked (edit the vars).
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
Changelog
|
Changelog
|
||||||
|
|
||||||
|
???:
|
||||||
|
- Added a commandline interface
|
||||||
|
|
||||||
1.1:
|
1.1:
|
||||||
- Added a button to open the mods/ folder
|
- Added a button to open the mods/ folder
|
||||||
- Added FTL resource extraction
|
- Added FTL resource extraction
|
||||||
|
|
11
skel_unix/modman-cli.sh
Normal file
11
skel_unix/modman-cli.sh
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Get the absolute path to this script's folder.
|
||||||
|
if echo "$0" | awk '{exit(!/^\//);}'; then
|
||||||
|
maindir=$(dirname "$0");
|
||||||
|
else
|
||||||
|
maindir=$PWD/$(dirname "$0");
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "${maindir}"
|
||||||
|
java -jar modman.jar "$@"
|
|
@ -81,11 +81,11 @@ public class FTLDat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
try {if (is != null) is.close();}
|
try {if ( is != null ) is.close();}
|
||||||
catch (IOException e) {}
|
catch ( IOException e ) {}
|
||||||
|
|
||||||
try {if (os != null) os.close();}
|
try {if ( os != null ) os.close();}
|
||||||
catch (IOException e) {}
|
catch ( IOException e ) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,8 +368,8 @@ public class FTLDat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
try {if (os != null) os.close();}
|
try {if ( os != null ) os.close();}
|
||||||
catch (IOException e) {}
|
catch ( IOException e ) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,8 +388,8 @@ public class FTLDat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
try {if (is != null) is.close();}
|
try {if ( is != null ) is.close();}
|
||||||
catch (IOException e) {}
|
catch ( IOException e ) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import javax.swing.JOptionPane;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
|
import net.vhati.modmanager.cli.SlipstreamCLI;
|
||||||
import net.vhati.modmanager.core.ComparableVersion;
|
import net.vhati.modmanager.core.ComparableVersion;
|
||||||
import net.vhati.modmanager.core.FTLUtilities;
|
import net.vhati.modmanager.core.FTLUtilities;
|
||||||
import net.vhati.modmanager.ui.ManagerFrame;
|
import net.vhati.modmanager.ui.ManagerFrame;
|
||||||
|
@ -25,13 +26,14 @@ public class FTLModManager {
|
||||||
|
|
||||||
private static final Logger log = LogManager.getLogger(FTLModManager.class);
|
private static final Logger log = LogManager.getLogger(FTLModManager.class);
|
||||||
|
|
||||||
private static final String APP_NAME = "Slipstream Mod Manager";
|
public static final String APP_NAME = "Slipstream Mod Manager";
|
||||||
private static final ComparableVersion APP_VERSION = new ComparableVersion( "???" );
|
public static final ComparableVersion APP_VERSION = new ComparableVersion( "???" );
|
||||||
private static final String APP_URL = "http://www.ftlgame.com/forum/viewtopic.php?f=12&t=17102";
|
public static final String APP_URL = "http://www.ftlgame.com/forum/viewtopic.php?f=12&t=17102";
|
||||||
private static final String APP_AUTHOR = "Vhati";
|
public static final String APP_AUTHOR = "Vhati";
|
||||||
|
|
||||||
|
|
||||||
public static void main( String[] args ) {
|
public static void main( String[] args ) {
|
||||||
|
if ( args.length > 0 ) SlipstreamCLI.main( args );
|
||||||
|
|
||||||
log.debug( String.format( "%s v%s", APP_NAME, APP_VERSION ) );
|
log.debug( String.format( "%s v%s", APP_NAME, APP_VERSION ) );
|
||||||
log.debug( String.format( "%s %s", System.getProperty("os.name"), System.getProperty("os.version") ) );
|
log.debug( String.format( "%s %s", System.getProperty("os.name"), System.getProperty("os.version") ) );
|
||||||
|
|
486
src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java
Normal file
486
src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java
Normal file
|
@ -0,0 +1,486 @@
|
||||||
|
package net.vhati.modmanager.cli;
|
||||||
|
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileFilter;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
import net.vhati.ftldat.FTLDat;
|
||||||
|
import net.vhati.modmanager.FTLModManager;
|
||||||
|
import net.vhati.modmanager.core.DelayedDeleteHook;
|
||||||
|
import net.vhati.modmanager.core.FTLUtilities;
|
||||||
|
import net.vhati.modmanager.core.ModPatchObserver;
|
||||||
|
import net.vhati.modmanager.core.ModPatchThread;
|
||||||
|
import net.vhati.modmanager.core.ModPatchThread.BackedUpDat;
|
||||||
|
import net.vhati.modmanager.core.ModUtilities;
|
||||||
|
import net.vhati.modmanager.core.Report;
|
||||||
|
import net.vhati.modmanager.core.Report.ReportFormatter;
|
||||||
|
import net.vhati.modmanager.core.Report.ReportMessage;
|
||||||
|
|
||||||
|
import org.apache.commons.cli.BasicParser;
|
||||||
|
import org.apache.commons.cli.OptionBuilder;
|
||||||
|
import org.apache.commons.cli.Options;
|
||||||
|
import org.apache.commons.cli.CommandLine;
|
||||||
|
import org.apache.commons.cli.HelpFormatter;
|
||||||
|
import org.apache.commons.cli.ParseException;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
|
||||||
|
public class SlipstreamCLI {
|
||||||
|
|
||||||
|
private static final Logger log = LogManager.getLogger(SlipstreamCLI.class);
|
||||||
|
|
||||||
|
private static File backupDir = new File( "./backup/" );
|
||||||
|
private static File modsDir = new File( "./mods/" );
|
||||||
|
|
||||||
|
|
||||||
|
public static void main( String[] args ) {
|
||||||
|
|
||||||
|
BasicParser parser = new BasicParser();
|
||||||
|
|
||||||
|
Options options = new Options();
|
||||||
|
options.addOption( OptionBuilder.withLongOpt( "extract-dats" )
|
||||||
|
.withDescription( "extract FTL resources into a dir" )
|
||||||
|
.hasArg()
|
||||||
|
.withArgName("DIR")
|
||||||
|
.create() );
|
||||||
|
options.addOption( OptionBuilder.withLongOpt( "list-mods" )
|
||||||
|
.withDescription( "list available mod names" )
|
||||||
|
.create() );
|
||||||
|
options.addOption( OptionBuilder.withLongOpt( "runftl" )
|
||||||
|
.withDescription( "run the game" )
|
||||||
|
.create() );
|
||||||
|
options.addOption( OptionBuilder.withLongOpt( "patch" )
|
||||||
|
.withDescription( "revert to vanilla and add named mods (if any)" )
|
||||||
|
.create() );
|
||||||
|
options.addOption( OptionBuilder.withLongOpt( "validate" )
|
||||||
|
.withDescription( "check named mods for problems" )
|
||||||
|
.create() );
|
||||||
|
options.addOption( "h", "help", false, "display this help and exit" );
|
||||||
|
options.addOption( OptionBuilder.withLongOpt( "version" )
|
||||||
|
.withDescription( "output version information and exit" )
|
||||||
|
.create() );
|
||||||
|
CommandLine cmdline = null;
|
||||||
|
try {
|
||||||
|
cmdline = parser.parse( options, args, true );
|
||||||
|
}
|
||||||
|
catch( ParseException e ) {
|
||||||
|
System.err.println( "Error parsing commandline: "+ e.getMessage() );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( cmdline.hasOption( "h" ) ) { // Exits.
|
||||||
|
HelpFormatter formatter = new HelpFormatter();
|
||||||
|
|
||||||
|
String helpHeader = "Perform actions against an FTL installation and/or a list of named mods."+ formatter.getNewLine();
|
||||||
|
|
||||||
|
String helpFooter = formatter.getNewLine();
|
||||||
|
helpFooter += "Each MODFILE is a filename in the mods/ dir."+ formatter.getNewLine();
|
||||||
|
helpFooter += "If a named mod is a directory, a temporary zip will be created.";
|
||||||
|
|
||||||
|
formatter.printHelp( "modman [OPTION] [MODFILE]...", helpHeader, options, helpFooter );
|
||||||
|
System.exit( 0 );
|
||||||
|
}
|
||||||
|
if ( cmdline.hasOption( "version" ) ) { // Exits.
|
||||||
|
System.out.println( getVersionMessage() );
|
||||||
|
System.exit( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
DelayedDeleteHook deleteHook = new DelayedDeleteHook();
|
||||||
|
Runtime.getRuntime().addShutdownHook( deleteHook );
|
||||||
|
|
||||||
|
if ( cmdline.hasOption( "validate" ) ) { // Exits (0/1).
|
||||||
|
log.info( "Validating..." );
|
||||||
|
|
||||||
|
StringBuilder resultBuf = new StringBuilder();
|
||||||
|
ReportFormatter formatter = new ReportFormatter();
|
||||||
|
boolean anyInvalid = false;
|
||||||
|
|
||||||
|
for ( String modFileName : cmdline.getArgs() ) {
|
||||||
|
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 \"%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" );
|
||||||
|
Properties config = getConfig( configFile );
|
||||||
|
|
||||||
|
if ( cmdline.hasOption( "list-mods" ) ) { // Exits.
|
||||||
|
log.info( "Listing mods..." );
|
||||||
|
|
||||||
|
boolean allowZip = config.getProperty( "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 );
|
||||||
|
|
||||||
|
System.exit( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
File datsDir = null;
|
||||||
|
if ( cmdline.hasOption( "extract-dats" ) ||
|
||||||
|
cmdline.hasOption( "patch" ) ||
|
||||||
|
cmdline.hasOption( "runftl" ) ) {
|
||||||
|
datsDir = getDatsDir( config );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( cmdline.hasOption( "extract-dats" ) ) { // Exits (0/1).
|
||||||
|
log.info( "Extracting dats..." );
|
||||||
|
|
||||||
|
String extractPath = cmdline.getOptionValue( "extract-dats" );
|
||||||
|
File extractDir = new File( extractPath );
|
||||||
|
|
||||||
|
File dataDatFile = new File( datsDir, "data.dat" );
|
||||||
|
File resDatFile = new File( datsDir, "resource.dat" );
|
||||||
|
File[] datFiles = new File[] {dataDatFile, resDatFile};
|
||||||
|
|
||||||
|
FTLDat.AbstractPack srcP = null;
|
||||||
|
FTLDat.AbstractPack dstP = null;
|
||||||
|
InputStream is = null;
|
||||||
|
try {
|
||||||
|
if ( !extractDir.exists() ) extractDir.mkdirs();
|
||||||
|
|
||||||
|
dstP = new FTLDat.FolderPack( extractDir );
|
||||||
|
|
||||||
|
for ( File datFile : datFiles ) {
|
||||||
|
srcP = new FTLDat.FTLPack( datFile, false );
|
||||||
|
List<String> innerPaths = srcP.list();
|
||||||
|
|
||||||
|
for ( String innerPath : innerPaths ) {
|
||||||
|
if ( dstP.contains( innerPath ) ) {
|
||||||
|
log.info( "While extracting resources, this file was overwritten: "+ innerPath );
|
||||||
|
dstP.remove( innerPath );
|
||||||
|
}
|
||||||
|
is = srcP.getInputStream( innerPath );
|
||||||
|
dstP.add( innerPath, is );
|
||||||
|
}
|
||||||
|
srcP.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 ( srcP != null ) srcP.close();}
|
||||||
|
catch ( IOException ex ) {}
|
||||||
|
|
||||||
|
try {if ( dstP != null ) dstP.close();}
|
||||||
|
catch ( IOException ex ) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.exit( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( cmdline.hasOption( "patch" ) ) { // Exits sometimes (1 on failure).
|
||||||
|
log.info( "Patching..." );
|
||||||
|
|
||||||
|
List<File> modFiles = new ArrayList<File>();
|
||||||
|
for ( String modFileName : cmdline.getArgs() ) {
|
||||||
|
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 \"%s/\".", modFile.getName() ), e );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modFiles.add( modFile );
|
||||||
|
}
|
||||||
|
|
||||||
|
BackedUpDat dataDat = new BackedUpDat();
|
||||||
|
dataDat.datFile = new File( datsDir, "data.dat" );
|
||||||
|
dataDat.bakFile = new File( backupDir, "data.dat.bak" );
|
||||||
|
BackedUpDat resDat = new BackedUpDat();
|
||||||
|
resDat.datFile = new File( datsDir, "resource.dat" );
|
||||||
|
resDat.bakFile = new File( backupDir, "resource.dat.bak" );
|
||||||
|
|
||||||
|
SilentPatchObserver patchObserver = new SilentPatchObserver();
|
||||||
|
ModPatchThread patchThread = new ModPatchThread( modFiles, dataDat, resDat, patchObserver );
|
||||||
|
deleteHook.addWatchedThread( patchThread );
|
||||||
|
|
||||||
|
patchThread.start();
|
||||||
|
while ( patchThread.isAlive() ) {
|
||||||
|
try {patchThread.join();}
|
||||||
|
catch ( InterruptedException e ) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !patchObserver.hasSucceeded() ) System.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( cmdline.hasOption( "runftl" ) ) { // Exits (0/1).
|
||||||
|
log.info( "Running FTL..." );
|
||||||
|
|
||||||
|
File exeFile = FTLUtilities.findGameExe( datsDir );
|
||||||
|
if ( exeFile != null ) {
|
||||||
|
try {
|
||||||
|
FTLUtilities.launchGame( exeFile );
|
||||||
|
} catch ( Exception e ) {
|
||||||
|
log.error( "Error launching FTL.", e );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.error( "Could not find FTL's executable." );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
System.exit( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Properties getConfig( File configFile ) {
|
||||||
|
|
||||||
|
Properties config = new Properties();
|
||||||
|
config.setProperty( "allow_zip", "false" );
|
||||||
|
config.setProperty( "ftl_dats_path", "" );
|
||||||
|
config.setProperty( "never_run_ftl", "false" );
|
||||||
|
config.setProperty( "use_default_ui", "false" );
|
||||||
|
// "update_catalog" 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 );
|
||||||
|
config.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 ) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the validity of the config's dats path and returns it.
|
||||||
|
* Or exits if the path is invalid.
|
||||||
|
*/
|
||||||
|
private static File getDatsDir( Properties config ) {
|
||||||
|
File datsDir = null;
|
||||||
|
String datsPath = config.getProperty( "ftl_dats_path", "" );
|
||||||
|
|
||||||
|
if ( datsPath.length() > 0 ) {
|
||||||
|
log.info( "Using FTL dats path from config: "+ datsPath );
|
||||||
|
datsDir = new File( datsPath );
|
||||||
|
if ( FTLUtilities.isDatsDirValid( datsDir ) == false ) {
|
||||||
|
log.error( "The config's ftl_dats_path does not exist." );
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a temporary zip made from a directory.
|
||||||
|
*
|
||||||
|
* 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" );
|
||||||
|
|
||||||
|
FileOutputStream fos = null;
|
||||||
|
try {
|
||||||
|
fos = new FileOutputStream( tempFile );
|
||||||
|
ZipOutputStream zos = new ZipOutputStream( new BufferedOutputStream( fos ) );
|
||||||
|
addDirToArchive( zos, dir, null );
|
||||||
|
zos.close();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
try {if ( fos != null ) fos.close();}
|
||||||
|
catch ( IOException e ) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addDirToArchive( ZipOutputStream zos, File dir, String pathPrefix ) throws IOException {
|
||||||
|
if ( pathPrefix == null ) pathPrefix = "";
|
||||||
|
|
||||||
|
for ( File f : dir.listFiles() ) {
|
||||||
|
if ( f.isDirectory() ) {
|
||||||
|
addDirToArchive( zos, f, pathPrefix + f.getName() +"/" );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInputStream is = null;
|
||||||
|
try {
|
||||||
|
is = new FileInputStream( f );
|
||||||
|
zos.putNextEntry( new ZipEntry( pathPrefix + f.getName() ) );
|
||||||
|
|
||||||
|
byte[] buf = new byte[4096];
|
||||||
|
int len;
|
||||||
|
while ( (len = is.read(buf)) >= 0 ) {
|
||||||
|
zos.write( buf, 0, len );
|
||||||
|
}
|
||||||
|
|
||||||
|
zos.closeEntry();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
try {if ( is != null ) is.close();}
|
||||||
|
catch ( IOException e ) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getVersionMessage() {
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
buf.append( String.format( "%s %s\n", FTLModManager.APP_NAME, FTLModManager.APP_VERSION ) );
|
||||||
|
buf.append( "Copyright (C) 2013 David Millis\n" );
|
||||||
|
buf.append( "\n" );
|
||||||
|
buf.append( "This program is free software; you can redistribute it and/or modify\n" );
|
||||||
|
buf.append( "it under the terms of the GNU General Public License as published by\n" );
|
||||||
|
buf.append( "the Free Software Foundation; version 2.\n" );
|
||||||
|
buf.append( "\n" );
|
||||||
|
buf.append( "This program is distributed in the hope that it will be useful,\n" );
|
||||||
|
buf.append( "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" );
|
||||||
|
buf.append( "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" );
|
||||||
|
buf.append( "GNU General Public License for more details.\n" );
|
||||||
|
buf.append( "\n" );
|
||||||
|
buf.append( "You should have received a copy of the GNU General Public License\n" );
|
||||||
|
buf.append( "along with this program. If not, see http://www.gnu.org/licenses/.\n" );
|
||||||
|
buf.append( "\n" );
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static class SilentPatchObserver implements ModPatchObserver {
|
||||||
|
private boolean done = false;
|
||||||
|
private boolean succeeded = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void patchingProgress( final int value, final int max ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void patchingStatus( String message ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void patchingMod( File modFile ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void patchingEnded( boolean outcome, Exception e ) {
|
||||||
|
succeeded = outcome;
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean isDone() { return done; }
|
||||||
|
public synchronized boolean hasSucceeded() { return succeeded; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static class ModAndDirFileFilter implements FileFilter {
|
||||||
|
private boolean allowZip;
|
||||||
|
private boolean allowDirs;
|
||||||
|
|
||||||
|
public ModAndDirFileFilter( boolean allowZip, boolean allowDirs ) {
|
||||||
|
this.allowZip = allowZip;
|
||||||
|
this.allowDirs = allowDirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accept( File f ) {
|
||||||
|
if ( f.isDirectory() ) return allowDirs;
|
||||||
|
|
||||||
|
if ( f.getName().endsWith(".ftl") ) return true;
|
||||||
|
|
||||||
|
if ( allowZip ) {
|
||||||
|
if ( f.getName().endsWith(".zip") ) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package net.vhati.modmanager.core;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A shutdown hook that waits on threads before deleting files.
|
||||||
|
*
|
||||||
|
* This hook's waiting will keep the VM alive until the threads complete.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* DelayedDeleteHook deleteHook = new DelayedDeleteHook();
|
||||||
|
* Runtime.getRuntime().addShutdownHook( deleteHook );
|
||||||
|
*/
|
||||||
|
public class DelayedDeleteHook extends Thread {
|
||||||
|
private LinkedHashSet<Thread> watchedThreads = new LinkedHashSet<Thread>();
|
||||||
|
private LinkedHashSet<File> doomedFiles = new LinkedHashSet<File>();
|
||||||
|
|
||||||
|
public synchronized void addWatchedThread( Thread t ) {
|
||||||
|
if ( watchedThreads == null )
|
||||||
|
throw new IllegalStateException( "Shutdown in progress" );
|
||||||
|
watchedThreads.add( t );
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void addDoomedFile( File f ) {
|
||||||
|
if ( doomedFiles == null )
|
||||||
|
throw new IllegalStateException( "Shutdown in progress" );
|
||||||
|
doomedFiles.add( f );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
ArrayList<Thread> pendingThreads;
|
||||||
|
ArrayList<File> pendingFiles;
|
||||||
|
boolean interrupted = false;
|
||||||
|
|
||||||
|
synchronized ( this ) {
|
||||||
|
pendingThreads = new ArrayList<Thread>( watchedThreads );
|
||||||
|
pendingFiles = new ArrayList<File>( doomedFiles );
|
||||||
|
watchedThreads = null;
|
||||||
|
doomedFiles = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Wait on each thread.
|
||||||
|
Iterator<Thread> it = pendingThreads.iterator();
|
||||||
|
while ( it.hasNext() ) {
|
||||||
|
Thread t = it.next();
|
||||||
|
while ( t.isAlive() ) {
|
||||||
|
try {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
catch ( InterruptedException e ) {
|
||||||
|
interrupted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.reverse( pendingFiles );
|
||||||
|
for ( File f : pendingFiles ) {
|
||||||
|
f.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if ( interrupted ) Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,5 +31,5 @@ public interface ModPatchObserver {
|
||||||
* Patching ended.
|
* Patching ended.
|
||||||
* If anything went wrong, e may be non-null.
|
* If anything went wrong, e may be non-null.
|
||||||
*/
|
*/
|
||||||
public void patchingEnded( boolean success, Exception e );
|
public void patchingEnded( boolean outcome, Exception e );
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,10 +63,9 @@ public class ModPatchThread extends Thread {
|
||||||
keepRunning = false;
|
keepRunning = false;
|
||||||
boolean interrupted = false;
|
boolean interrupted = false;
|
||||||
try {
|
try {
|
||||||
while ( true ) {
|
while ( ModPatchThread.this.isAlive() ) {
|
||||||
try {
|
try {
|
||||||
ModPatchThread.this.join();
|
ModPatchThread.this.join();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
catch ( InterruptedException e ) {
|
catch ( InterruptedException e ) {
|
||||||
interrupted = true;
|
interrupted = true;
|
||||||
|
@ -84,7 +83,7 @@ public class ModPatchThread extends Thread {
|
||||||
result = patch();
|
result = patch();
|
||||||
}
|
}
|
||||||
catch ( Exception e ) {
|
catch ( Exception e ) {
|
||||||
log.error( "Patching failed. See log for details.", e );
|
log.error( "Patching failed.", e );
|
||||||
exception = e;
|
exception = e;
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import java.io.InputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.PrintWriter;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
@ -50,6 +51,13 @@ import org.jdom2.input.SAXBuilder;
|
||||||
import org.jdom2.output.Format;
|
import org.jdom2.output.Format;
|
||||||
import org.jdom2.output.XMLOutputter;
|
import org.jdom2.output.XMLOutputter;
|
||||||
|
|
||||||
|
import org.apache.commons.cli.BasicParser;
|
||||||
|
import org.apache.commons.cli.OptionBuilder;
|
||||||
|
import org.apache.commons.cli.Options;
|
||||||
|
import org.apache.commons.cli.CommandLine;
|
||||||
|
import org.apache.commons.cli.HelpFormatter;
|
||||||
|
import org.apache.commons.cli.ParseException;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
@ -69,83 +77,137 @@ public class ForumScraper {
|
||||||
ignoredURLs.add( "http://www.ftlgame.com/forum/viewtopic.php?f=12&t=11083" );
|
ignoredURLs.add( "http://www.ftlgame.com/forum/viewtopic.php?f=12&t=11083" );
|
||||||
ignoredURLs.add( "http://www.ftlgame.com/forum/viewtopic.php?f=4&t=2938" );
|
ignoredURLs.add( "http://www.ftlgame.com/forum/viewtopic.php?f=4&t=2938" );
|
||||||
ignoredURLs.add( "http://www.moddb.com/mods/better-planets-and-backgrounds/downloads/better-asteroids" );
|
ignoredURLs.add( "http://www.moddb.com/mods/better-planets-and-backgrounds/downloads/better-asteroids" );
|
||||||
|
ignoredURLs.add( "http://www.ftlgame.com/forum/viewtopic.php?f=4&t=2947" );
|
||||||
|
ignoredURLs.add( "http://www.ftlgame.com/forum/viewtopic.php?f=12&t=11604" );
|
||||||
// SpaceDock is an app.
|
// SpaceDock is an app.
|
||||||
ignoredURLs.add( "http://www.ftlgame.com/forum/viewtopic.php?f=11&t=16842" );
|
ignoredURLs.add( "http://www.ftlgame.com/forum/viewtopic.php?f=11&t=16842" );
|
||||||
// Beginning Scrap Advantage is bundled in GMM.
|
// Beginning Scrap Advantage is bundled in GMM.
|
||||||
ignoredURLs.add( "http://www.ftlgame.com/forum/viewtopic.php?f=4&t=2464" );
|
ignoredURLs.add( "http://www.ftlgame.com/forum/viewtopic.php?f=4&t=2464" );
|
||||||
|
|
||||||
List<Option> options = new ArrayList<Option>();
|
|
||||||
options.add( new Option( "load-json", "--load-json", null, true, true ) );
|
|
||||||
options.add( new Option( "load-xml", "--load-xml", null, true, true ) );
|
|
||||||
options.add( new Option( "scrape", "--scrape", null, true, true ) );
|
|
||||||
options.add( new Option( "dump-json", "--dump-json", null, true, true ) );
|
|
||||||
options.add( new Option( "dump-xml", "--dump-xml", null, true, true ) );
|
|
||||||
options.add( new Option( "hash-thread", "--hash-thread", null, true, true ) );
|
|
||||||
options.add( new Option( "first-post", "--first-post", null, true, true ) );
|
|
||||||
options.add( new Option( "help", "--help", "-h", true, true ) );
|
|
||||||
|
|
||||||
List<String[]> tasks = parseArgs( args, options );
|
BasicParser parser = new BasicParser();
|
||||||
|
|
||||||
if ( tasks.isEmpty() )
|
Options options = new Options();
|
||||||
tasks.add( new String[] {"help"} );
|
options.addOption( OptionBuilder.withLongOpt( "load-json" )
|
||||||
|
.withDescription( "load moddb from a json catalog" )
|
||||||
|
.hasArg()
|
||||||
|
.withArgName("FILE")
|
||||||
|
.create() );
|
||||||
|
options.addOption( OptionBuilder.withLongOpt( "load-xml" )
|
||||||
|
.withDescription( "load moddb from an xml file" )
|
||||||
|
.hasArg()
|
||||||
|
.withArgName("FILE")
|
||||||
|
.create() );
|
||||||
|
options.addOption( OptionBuilder.withLongOpt( "scrape" )
|
||||||
|
.withDescription( "write changed forum posts to an xml file" )
|
||||||
|
.hasArg()
|
||||||
|
.withArgName("FILE")
|
||||||
|
.create() );
|
||||||
|
options.addOption( OptionBuilder.withLongOpt( "dump-json" )
|
||||||
|
.withDescription( "write the moddb to a json file" )
|
||||||
|
.hasArg()
|
||||||
|
.withArgName("FILE")
|
||||||
|
.create() );
|
||||||
|
options.addOption( OptionBuilder.withLongOpt( "dump-xml" )
|
||||||
|
.withDescription( "write the moddb to an xml file" )
|
||||||
|
.hasArg()
|
||||||
|
.withArgName("FILE")
|
||||||
|
.create() );
|
||||||
|
options.addOption( OptionBuilder.withLongOpt( "hash-thread" )
|
||||||
|
.withDescription( "print the hash of a specific thread" )
|
||||||
|
.hasArg()
|
||||||
|
.withArgName("URL")
|
||||||
|
.create() );
|
||||||
|
options.addOption( OptionBuilder.withLongOpt( "first-post" )
|
||||||
|
.withDescription( "print the first post of a thread (debugging)" )
|
||||||
|
.hasArg()
|
||||||
|
.withArgName("URL")
|
||||||
|
.create() );
|
||||||
|
options.addOption( "h", "help", false, "display this help and exit" );
|
||||||
|
|
||||||
|
CommandLine cmdline = null;
|
||||||
|
try {
|
||||||
|
cmdline = parser.parse( options, args, true );
|
||||||
|
}
|
||||||
|
catch( ParseException e ) {
|
||||||
|
System.err.println( "Error parsing commandline: "+ e.getMessage() );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( cmdline.hasOption( "h" ) ) {
|
||||||
|
HelpFormatter formatter = new HelpFormatter();
|
||||||
|
|
||||||
|
String syntax = ForumScraper.class.getCanonicalName() +" [OPTIONS]";
|
||||||
|
|
||||||
|
String helpHeader = "Load an existing catalog as the moddb, and scrape."+ formatter.getNewLine();
|
||||||
|
helpHeader += "Edit the catalog by copy/pasting scrape snippets."+ formatter.getNewLine();
|
||||||
|
helpHeader += "Load the edited catalog and dump json."+ formatter.getNewLine();
|
||||||
|
|
||||||
|
PrintWriter pw = new PrintWriter( System.out );
|
||||||
|
formatter.printUsage( pw, formatter.getWidth(), syntax );
|
||||||
|
pw.write( helpHeader );
|
||||||
|
pw.write( formatter.getNewLine() );
|
||||||
|
formatter.printOptions( pw, formatter.getWidth(), options, formatter.getLeftPadding(), formatter.getDescPadding() );
|
||||||
|
pw.flush();
|
||||||
|
|
||||||
|
System.exit( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
ModDB modDB = new ModDB();
|
ModDB modDB = new ModDB();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for ( String[] task : tasks ) {
|
if ( cmdline.hasOption( "load-json" ) ) {
|
||||||
if ( task[0].equals( "load-json" ) ) {
|
log.info( "Loading json catalog..." );
|
||||||
log.info( "Loading json catalog..." );
|
|
||||||
ModDB newDB = JacksonGrognakCatalogReader.parse( new File(task[1]) );
|
File srcFile = new File( cmdline.getOptionValue( "load-json" ) );
|
||||||
if ( newDB != null ) modDB = newDB;
|
ModDB newDB = JacksonGrognakCatalogReader.parse( srcFile );
|
||||||
}
|
if ( newDB != null ) modDB = newDB;
|
||||||
else if ( task[0].equals( "load-xml" ) ) {
|
}
|
||||||
log.info( "Loading xml catalog..." );
|
|
||||||
ModDB newDB = parseCatalogXML( new File(task[1]) );
|
if ( cmdline.hasOption( "load-xml" ) ) {
|
||||||
if ( newDB != null ) modDB = newDB;
|
log.info( "Loading xml catalog..." );
|
||||||
}
|
|
||||||
else if ( task[0].equals( "scrape" ) ) {
|
File srcFile = new File( cmdline.getOptionValue( "load-xml" ) );
|
||||||
log.info( "Scraping..." );
|
ModDB newDB = parseCatalogXML( srcFile );
|
||||||
List<ModsInfo> data = scrape( modDB, MASTER_LIST_URL, ignoredURLs );
|
if ( newDB != null ) modDB = newDB;
|
||||||
if ( data.size() > 0 ) writeXML( data, new File(task[1]) );
|
}
|
||||||
}
|
|
||||||
else if ( task[0].equals( "dump-json" ) ) {
|
if ( cmdline.hasOption( "scrape" ) ) {
|
||||||
log.info( "Dumping json..." );
|
log.info( "Scraping..." );
|
||||||
List<ModsInfo> data = getCollatedModInfo( modDB );
|
|
||||||
if ( data.size() > 0 ) writeJSON( data, new File(task[1]) );
|
File dstFile = new File( cmdline.getOptionValue( "scrape" ) );
|
||||||
}
|
List<ModsInfo> data = scrape( modDB, MASTER_LIST_URL, ignoredURLs );
|
||||||
else if ( task[0].equals( "dump-xml" ) ) {
|
if ( data.size() > 0 ) writeXML( data, dstFile );
|
||||||
log.info( "Dumping xml..." );
|
}
|
||||||
List<ModsInfo> data = getCollatedModInfo( modDB );
|
|
||||||
if ( data.size() > 0 ) writeXML( data, new File(task[1]) );
|
if ( cmdline.hasOption( "dump-json" ) ) {
|
||||||
}
|
log.info( "Dumping json..." );
|
||||||
else if ( task[0].equals( "hash-thread" ) ) {
|
|
||||||
log.info( "Hashing thread..." );
|
File dstFile = new File( cmdline.getOptionValue( "dump-json" ) );
|
||||||
System.out.println( hashThread( task[1] ) );
|
List<ModsInfo> data = getCollatedModInfo( modDB );
|
||||||
}
|
if ( data.size() > 0 ) writeJSON( data, dstFile );
|
||||||
else if ( task[0].equals( "first-post" ) ) {
|
}
|
||||||
log.info( "Getting thread's first post..." );
|
|
||||||
System.out.println( getFirstPost( task[1] ) );
|
if ( cmdline.hasOption( "dump-xml" ) ) {
|
||||||
}
|
log.info( "Dumping xml..." );
|
||||||
else if ( task[0].equals( "help" ) ) {
|
|
||||||
System.out.println( "Usage: java -cp modman.jar net.vhati.modmanager.scraper.ForumScraper [OPTION]" );
|
File dstFile = new File( cmdline.getOptionValue( "dump-xml" ) );
|
||||||
System.out.println( "" );
|
List<ModsInfo> data = getCollatedModInfo( modDB );
|
||||||
System.out.println( "Load an existing catalog as the moddb, and scrape." );
|
if ( data.size() > 0 ) writeXML( data, dstFile );
|
||||||
System.out.println( "Edit the catalog by copy/pasting scrape snippets." );
|
}
|
||||||
System.out.println( "Load the edited catalog and dump json." );
|
|
||||||
System.out.println( "" );
|
if ( cmdline.hasOption( "hash-thread" ) ) {
|
||||||
System.out.println( " --load-json FILE load moddb from a json GMM catalog" );
|
log.info( "Hashing thread..." );
|
||||||
System.out.println( " --load-xml FILE load moddb from an xml file" );
|
|
||||||
System.out.println( " --scrape FILE write changed forum posts to an xml file" );
|
String threadURL = cmdline.getOptionValue( "hash-thread" );
|
||||||
System.out.println( " --dump-json FILE write the moddb to a json file" );
|
System.out.println( hashThread( threadURL ) );
|
||||||
System.out.println( " --dump-xml FILE write the moddb to an xml file" );
|
}
|
||||||
System.out.println( "" );
|
|
||||||
System.out.println( " --hash-thread URL print the hash of a specific thread" );
|
if ( cmdline.hasOption( "first-post" ) ) {
|
||||||
System.out.println( " --first-post URL print the first post of a thread (debugging)" );
|
log.info( "Getting thread's first post..." );
|
||||||
System.out.println( "" );
|
|
||||||
System.out.println( " -h, --help display this help and exit" );
|
String threadURL = cmdline.getOptionValue( "first-post" );
|
||||||
System.out.println( "" );
|
System.out.println( getFirstPost( threadURL ) );
|
||||||
System.out.println( "" );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch ( Exception e ) {
|
catch ( Exception e ) {
|
||||||
|
@ -154,53 +216,6 @@ public class ForumScraper {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* I want to take args, but not badly enough for another library. :P
|
|
||||||
*/
|
|
||||||
private static List<String[]> parseArgs( String[] args, List<Option> options ) {
|
|
||||||
List<String[]> tasks = new ArrayList<String[]>();
|
|
||||||
|
|
||||||
for ( int i=0; i < args.length; ) {
|
|
||||||
boolean foundOption = false;
|
|
||||||
|
|
||||||
for ( Option opt : options ) {
|
|
||||||
String[] result = null;
|
|
||||||
boolean matches = false;
|
|
||||||
if ( opt.shortName != null && opt.shortName.equals(args[i]) ) matches = true;
|
|
||||||
if ( opt.longName != null && opt.longName.equals(args[i]) ) matches = true;
|
|
||||||
if ( matches ) {
|
|
||||||
foundOption = true;
|
|
||||||
if ( opt.acceptsValue ) {
|
|
||||||
result = new String[] {opt.action, null};
|
|
||||||
|
|
||||||
if ( i+1 < args.length && !args[i+1].startsWith("-") ) {
|
|
||||||
result[1] = args[i+1];
|
|
||||||
i += 2;
|
|
||||||
} else if ( opt.requiresValue ) {
|
|
||||||
log.error( String.format( "The \"%s\" option requires an argument.", args[i] ) );
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
tasks.add( result );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
result = new String[] {opt.action};
|
|
||||||
tasks.add( result );
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( foundOption ) break;
|
|
||||||
}
|
|
||||||
if ( !foundOption ) {
|
|
||||||
log.info( "Unrecognized option: "+ args[i] );
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tasks;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collects ModInfo objects that differ only in version, and creates ModsInfo objects.
|
* Collects ModInfo objects that differ only in version, and creates ModsInfo objects.
|
||||||
*/
|
*/
|
||||||
|
@ -365,7 +380,7 @@ public class ForumScraper {
|
||||||
log.error( "Request failed: "+ e.getMessage() );
|
log.error( "Request failed: "+ e.getMessage() );
|
||||||
}
|
}
|
||||||
try {Thread.sleep( 5000 );}
|
try {Thread.sleep( 5000 );}
|
||||||
catch ( InterruptedException e ) {log.info( "Re-fetch sleep interrupted." );}
|
catch ( InterruptedException e ) {log.error( "Re-fetch sleep interrupted.", e );}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,21 +711,4 @@ public class ForumScraper {
|
||||||
versions.add( new String[] {fileHash, fileVersion} );
|
versions.add( new String[] {fileHash, fileVersion} );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A potential commandline option. */
|
|
||||||
private static class Option {
|
|
||||||
String action;
|
|
||||||
String longName;
|
|
||||||
String shortName;
|
|
||||||
boolean acceptsValue;
|
|
||||||
boolean requiresValue;
|
|
||||||
|
|
||||||
public Option( String action, String longName, String shortName, boolean acceptsValue, boolean requiresValue ) {
|
|
||||||
this.action = action;
|
|
||||||
this.longName = longName;
|
|
||||||
this.shortName = shortName;
|
|
||||||
this.acceptsValue = acceptsValue;
|
|
||||||
this.requiresValue = requiresValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -724,7 +724,7 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver
|
||||||
if ( exeFile != null ) {
|
if ( exeFile != null ) {
|
||||||
int response = JOptionPane.showConfirmDialog( ManagerFrame.this, "Do you want to run the game now?", "Ready to Play", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE );
|
int response = JOptionPane.showConfirmDialog( ManagerFrame.this, "Do you want to run the game now?", "Ready to Play", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE );
|
||||||
if ( response == JOptionPane.YES_OPTION ) {
|
if ( response == JOptionPane.YES_OPTION ) {
|
||||||
log.info( "Launching FTL..." );
|
log.info( "Running FTL..." );
|
||||||
try {
|
try {
|
||||||
FTLUtilities.launchGame( exeFile );
|
FTLUtilities.launchGame( exeFile );
|
||||||
} catch ( Exception e ) {
|
} catch ( Exception e ) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<configuration status="warn">
|
<configuration status="warn">
|
||||||
<appenders>
|
<appenders>
|
||||||
<Console name="Console" target="SYSTEM_OUT">
|
<Console name="Console" target="SYSTEM_ERR">
|
||||||
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY" />
|
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY" />
|
||||||
<PatternLayout>
|
<PatternLayout>
|
||||||
<pattern>%-5level %logger{1} - %msg%throwable%n</pattern>
|
<pattern>%-5level %logger{1} - %msg%throwable%n</pattern>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue