Added user-defined steam location and Windows registry awareness

This commit is contained in:
Vhati 2017-12-10 03:47:25 -05:00
parent 1603a7fc56
commit 85b9405b8f
5 changed files with 234 additions and 26 deletions

View file

@ -6,6 +6,7 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Properties;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
@ -69,6 +70,7 @@ public class FTLModManager {
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" );
@ -185,21 +187,90 @@ public class FTLModManager {
throw new ExitException();
}
// Ask about Steam.
if ( appConfig.getProperty( SlipstreamConfig.STEAM_EXE_PATH, "" ).length() == 0 ) {
int steamBasedResponse = JOptionPane.showConfirmDialog( null, "Was FTL installed via Steam?", "Confirm", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE );
if ( steamBasedResponse == JOptionPane.YES_OPTION ) {
File steamExeFile = FTLUtilities.findSteamExe();
if ( steamExeFile == null && System.getProperty( "os.name" ).startsWith( "Windows" ) ) {
try {
String registryExePath = FTLUtilities.queryRegistryKey( "HKCU\\Software\\Valve\\Steam", "SteamExe", "REG_SZ" );
if ( registryExePath != null && !(steamExeFile=new File( registryExePath )).exists() ) {
steamExeFile = null;
}
}
catch( IOException e ) {
log.error( "Error while querying registry for Steam's path", e );
}
}
if ( steamExeFile != null ) {
int response = JOptionPane.showConfirmDialog( null, "Steam was found at:\n"+ steamExeFile.getPath() +"\nIs this correct?", "Confirm", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE );
if ( response == JOptionPane.NO_OPTION ) steamExeFile = null;
}
if ( steamExeFile == null ) {
log.debug( "Steam was not located automatically. Prompting user for location" );
StringBuilder steamBuf = new StringBuilder();
steamBuf.append( "You will be prompted to locate Steam's executable.\n" );
steamBuf.append( "- Windows: Steam.exe\n" );
steamBuf.append( "- Linux: steam\n" );
steamBuf.append( "- OSX: Steam.app\n" );
steamBuf.append( "\n" );
steamBuf.append( "If you can't find it, you can cancel and set it later." );
JOptionPane.showMessageDialog( null, steamBuf.toString(), "Find Steam", JOptionPane.INFORMATION_MESSAGE );
JFileChooser steamExeChooser = new JFileChooser();
steamExeChooser.setDialogTitle( "Find Steam.exe or steam or Steam.app" );
steamExeChooser.setFileHidingEnabled( false );
steamExeChooser.setMultiSelectionEnabled( false );
if ( steamExeChooser.showOpenDialog( null ) == JFileChooser.APPROVE_OPTION ) {
steamExeFile = steamExeChooser.getSelectedFile();
if ( !steamExeFile.exists() ) steamExeFile = null;
}
}
if ( steamExeFile != null ) {
appConfig.setProperty( SlipstreamConfig.STEAM_EXE_PATH, steamExeFile.getAbsolutePath() );
writeConfig = true;
log.info( "Steam located at: "+ steamExeFile.getAbsolutePath() );
}
if ( appConfig.getProperty( SlipstreamConfig.STEAM_EXE_PATH, "" ).length() > 0 ) {
String[] launchOptions = new String[] {"Directly", "Steam"};
int launchResponse = JOptionPane.showOptionDialog( null, "Would you prefer to launch FTL directly, or via Steam?", "How to Launch?", JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, launchOptions, launchOptions[1] );
if ( launchResponse == 0 ) {
appConfig.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" );
writeConfig = true;
}
else if ( launchResponse == 1 ) {
appConfig.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "true" );
writeConfig = true;
}
}
}
else {
appConfig.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" );
writeConfig = true;
}
}
// Prompt if update_catalog is invalid or hasn't been set.
boolean askAboutUpdates = false;
String catalogUpdateInterval = appConfig.getProperty( SlipstreamConfig.UPDATE_CATALOG );
String appUpdateInterval = appConfig.getProperty( SlipstreamConfig.UPDATE_APP );
if ( catalogUpdateInterval == null || !catalogUpdateInterval.matches( "^\\d+$" ) )
if ( !appConfig.getProperty( SlipstreamConfig.UPDATE_CATALOG, "" ).matches( "^\\d+$" ) )
askAboutUpdates = true;
if ( appUpdateInterval == null || !appUpdateInterval.matches( "^\\d+$" ) )
if ( !appConfig.getProperty( SlipstreamConfig.UPDATE_APP, "" ).matches( "^\\d+$" ) )
askAboutUpdates = true;
if ( askAboutUpdates ) {
String message = "";
message += "Would you like Slipstream to periodically\n";
message += "check for updates and download descriptions\n";
message += "for the latest mods?\n\n";
message += "Would you like Slipstream to periodically check for updates?\n";
message += "\n";
message += "You can change this later in modman.cfg.";
int response = JOptionPane.showConfirmDialog( null, message, "Updates", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE );

View file

@ -1,10 +1,15 @@
package net.vhati.modmanager.core;
import java.awt.Component;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;
@ -89,7 +94,7 @@ public class FTLUtilities {
candidates.add( new File( xdgDataHome +"/Steam/steamapps/common/FTL Faster Than Light/data/resources" ) );
candidates.add( new File( xdgDataHome +"/Steam/SteamApps/common/FTL Faster Than Light/data/resources" ) );
}
if ( home != null ) {
if ( home != null ) { // I think .steam/ contains symlinks to the paths above.
candidates.add( new File( home +"/.steam/steam/steamapps/common/FTL Faster Than Light/data" ) );
candidates.add( new File( home +"/.steam/steam/SteamApps/common/FTL Faster Than Light/data" ) );
@ -150,7 +155,7 @@ public class FTLUtilities {
message += "Or select 'FTL.app', if you're on OSX.";
JOptionPane.showMessageDialog( parentComponent, message, "Find FTL", JOptionPane.INFORMATION_MESSAGE );
final JFileChooser fc = new JFileChooser();
JFileChooser fc = new JFileChooser();
fc.setDialogTitle( "Find ftl.dat or data.dat or FTL.app" );
fc.setFileHidingEnabled( false );
fc.addChoosableFileFilter(new FileFilter() {
@ -264,7 +269,12 @@ public class FTLUtilities {
* On Linux, "steam" is a script. ( http://moritzmolch.com/815 )
* On OSX, "Steam.app" is a bundle.
*
* The definitive Windows registry will not be checked.
* Key,Name,Type: "HKCU\\Software\\Valve\\Steam", "SteamExe", "REG_SZ".
*
* The args to launch FTL are: ["-applaunch", STEAM_APPID_FTL]
*
* @see #queryRegistryKey(String, String, String)
*/
public static File findSteamExe() {
String programFiles86 = System.getenv( "ProgramFiles(x86)" );
@ -301,6 +311,27 @@ public class FTLUtilities {
return result;
}
/**
* Tells Steam to "verify game cache".
*
* This will spawn a process to notify Steam and exit immediately.
*
* Steam will start, if not already running, and a popup with progress bar
* will appear.
*
* For FTL, this method amounts to running:
* Steam.exe "steam://validate/212680"
*
* Steam registers itself with the OS as a custom URI handler. The URI gets
* passed as an argument when a "steam://" address is visited.
*/
public static Process verifySteamGameCache( File exeFile, String appId ) throws IOException {
if ( appId == null || appId.length() == 0 ) throw new IllegalArgumentException( "No Steam APP_ID was provided" );
String[] exeArgs = new String[] {"steam://validate/"+ appId};
return launchExe( exeFile, exeArgs );
}
/**
* Launches an executable.
*
@ -382,4 +413,63 @@ public class FTLUtilities {
return result;
}
/**
* Returns a value from the Windows registry, by scraping reg.exe, or null.
*
* This is equivalent to: reg.exe query {key} /v {valueName} /t {valueType}
*
* This view will not be jailed in Wow6432Node, even if Java is?
* Characters outside windows-1252 are unsupported (results will be mangled).
*
* Bad unicode example: "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Console\\TrueTypeFont", "932", "REG_SZ".
*
* @param key a backslash path starting with HKLM, HKCU, HKCR, HKU, HKCC
* @param valueName a value name, or "" for the "(Default)" value
* @param valueType REG_SZ ("Abc"), REG_DWORD ("0x1"), REG_BINARY ("44E09C"), etc
*/
public static String queryRegistryKey( String key, String valueName, String valueType ) throws IOException {
if ( !System.getProperty( "os.name" ).startsWith( "Windows" ) ) return null;
if ( key == null || valueType == null || key.length() * valueType.length() == 0 ) {
throw new IllegalArgumentException( "key and valueType cannot be null or empty" );
}
BufferedReader r = null;
try {
String regExePath = "reg.exe";
String winDir = System.getenv( "windir" );
if ( winDir != null && winDir.length() > 0 ) {
// When Java's in Wow64 redirection jail, sysnative is a virtual dir with the 64bit commands.
// I don't know if this will ever happen to Java.
File unWowRegExeFile = new File( winDir, "sysnative\\reg.exe" );
if ( unWowRegExeFile.exists() ) regExePath = unWowRegExeFile.getAbsolutePath();
}
String[] steamRegArgs = new String[] {regExePath, "query", key, "/v", valueName, "/t", valueType};
Pattern regPtn = Pattern.compile( Pattern.quote( (( valueName != null ) ? valueName : "(Default)") ) +"\\s+"+ Pattern.quote( valueType ) +"\\s+(.*)" );
Process p = new ProcessBuilder( steamRegArgs ).start();
p.waitFor();
if ( p.exitValue() == 0 ) {
r = new BufferedReader( new InputStreamReader( p.getInputStream(), "windows-1252" ) );
Matcher m;
String line;
while ( (line=r.readLine()) != null ) {
if ( (m=regPtn.matcher( line )).find() ) {
return m.group( 1 );
}
}
}
}
catch ( InterruptedException e ) { // *shrug*
Thread.currentThread().interrupt(); // Set interrupt flag.
}
finally {
try {if ( r != null ) r.close();}
catch ( IOException e ) {}
}
return null;
}
}

View file

@ -16,6 +16,7 @@ public class SlipstreamConfig {
public static final String ALLOW_ZIP = "allow_zip";
public static final String FTL_DATS_PATH = "ftl_dats_path";
public static final String STEAM_EXE_PATH = "steam_exe_path";
public static final String RUN_STEAM_FTL = "run_steam_ftl";
public static final String NEVER_RUN_FTL = "never_run_ftl";
public static final String UPDATE_CATALOG = "update_catalog";
@ -80,6 +81,7 @@ public class SlipstreamConfig {
userFieldsMap.put( ALLOW_ZIP, "Sets whether to treat .zip files as .ftl files. Default: false." );
userFieldsMap.put( FTL_DATS_PATH, "The path to FTL's resources folder. If invalid, you'll be prompted." );
userFieldsMap.put( STEAM_EXE_PATH, "The path to Steam's executable, if FTL was installed via Steam." );
userFieldsMap.put( RUN_STEAM_FTL, "If true, SMM will use Steam to launch FTL, if possible. Default: false." );
userFieldsMap.put( NEVER_RUN_FTL, "If true, there will be no offer to run FTL after patching. Default: false." );
userFieldsMap.put( UPDATE_CATALOG, "If a number greater than 0, check for new mod descriptions every N days." );

View file

@ -193,7 +193,7 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
modsFolderBtn = new JButton( "Open mods/" );
modsFolderBtn.setMargin( actionInsets );
modsFolderBtn.addMouseListener( new StatusbarMouseListener( this, "Open the mods/ folder." ) );
modsFolderBtn.addMouseListener( new StatusbarMouseListener( this, String.format( "Open the %s/ folder.", modsDir.getName() ) ) );
modsFolderBtn.addActionListener( this );
modsFolderBtn.setEnabled( Desktop.isDesktopSupported() );
modActionsPanel.add( modsFolderBtn );
@ -258,7 +258,6 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
@Override
public void windowClosed( WindowEvent e ) {
// dispose() was called.
ListState<ModFileInfo> tableState = getCurrentModsTableState();
saveModsTableState( tableState );
@ -667,25 +666,38 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
ModPatchDialog patchDlg = new ModPatchDialog( this, true );
String neverRunFtl = appConfig.getProperty( SlipstreamConfig.NEVER_RUN_FTL, "false" );
if ( !neverRunFtl.equals( "true" ) ) {
// Offer to run FTL.
if ( !"true".equals( appConfig.getProperty( SlipstreamConfig.NEVER_RUN_FTL, "false" ) ) ) {
File exeFile = null;
String[] exeArgs = null;
if ( appConfig.getProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" ).equals( "true" ) ) {
exeFile = FTLUtilities.findSteamExe();
exeArgs = new String[] {"-applaunch", FTLUtilities.STEAM_APPID_FTL};
// Try to run via Steam.
if ( "true".equals( appConfig.getProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" ) ) ) {
String steamPath = appConfig.getProperty( SlipstreamConfig.STEAM_EXE_PATH );
if ( steamPath.length() > 0 ) {
exeFile = new File( steamPath );
if ( exeFile.exists() ) {
exeArgs = new String[] {"-applaunch", FTLUtilities.STEAM_APPID_FTL};
}
else {
log.warn( String.format( "%s does not exist: %s", SlipstreamConfig.STEAM_EXE_PATH, exeFile.getAbsolutePath() ) );
exeFile = null;
}
}
if ( exeFile == null ) {
log.warn( "Steam executable could not be found; FTL will be launched directly" );
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 );
exeArgs = new String[0];
if ( exeFile == null ) {
if ( exeFile != null ) {
exeArgs = new String[0];
} else {
log.warn( "FTL executable could not be found" );
}
}
@ -743,7 +755,7 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
if ( Desktop.isDesktopSupported() ) {
Desktop.getDesktop().open( modsDir.getCanonicalFile() );
} else {
log.error( "Opening the mods/ folder is not possible on your OS" );
log.error( String.format( "Java cannot open the %s/ folder for you on this OS", modsDir.getName() ) );
}
}
catch ( IOException f ) {

View file

@ -11,6 +11,7 @@ import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
@ -36,6 +37,7 @@ public class SlipstreamConfigDialog extends JFrame implements ActionListener {
protected static final String UPDATE_CATALOG = SlipstreamConfig.UPDATE_CATALOG;
protected static final String UPDATE_APP = SlipstreamConfig.UPDATE_APP;
protected static final String FTL_DATS_PATH = SlipstreamConfig.FTL_DATS_PATH;
protected static final String STEAM_EXE_PATH = SlipstreamConfig.STEAM_EXE_PATH;
protected SlipstreamConfig appConfig;
@ -76,16 +78,19 @@ public class SlipstreamConfigDialog extends JFrame implements ActionListener {
editorPanel.addRow( FTL_DATS_PATH, ContentType.CHOOSER );
editorPanel.addTextRow( "Path to FTL's resources folder." );
editorPanel.addSeparatorRow();
editorPanel.addRow( STEAM_EXE_PATH, ContentType.CHOOSER );
editorPanel.addTextRow( "Path to Steam's executable." );
editorPanel.addSeparatorRow();
editorPanel.addBlankRow();
editorPanel.addTextRow( "Note: Some changes may have no immediate effect." );
editorPanel.addBlankRow();
editorPanel.addFillRow();
editorPanel.getBoolean( ALLOW_ZIP ).setSelected( appConfig.getProperty( SlipstreamConfig.ALLOW_ZIP, "false" ).equals( "true" ) );
editorPanel.getBoolean( RUN_STEAM_FTL ).setSelected( appConfig.getProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" ).equals( "true" ) );
editorPanel.getBoolean( NEVER_RUN_FTL ).setSelected( appConfig.getProperty( SlipstreamConfig.NEVER_RUN_FTL, "false" ).equals( "true" ) );
editorPanel.getBoolean( USE_DEFAULT_UI ).setSelected( appConfig.getProperty( SlipstreamConfig.USE_DEFAULT_UI, "false" ).equals( "true" ) );
editorPanel.getBoolean( REMEMBER_GEOMETRY ).setSelected( appConfig.getProperty( SlipstreamConfig.REMEMBER_GEOMETRY, "true" ).equals( "true" ) );
editorPanel.getBoolean( ALLOW_ZIP ).setSelected( "true".equals( appConfig.getProperty( SlipstreamConfig.ALLOW_ZIP, "false" ) ) );
editorPanel.getBoolean( RUN_STEAM_FTL ).setSelected( "true".equals( appConfig.getProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" ) ) );
editorPanel.getBoolean( NEVER_RUN_FTL ).setSelected( "true".equals( appConfig.getProperty( SlipstreamConfig.NEVER_RUN_FTL, "false" ) ) );
editorPanel.getBoolean( USE_DEFAULT_UI ).setSelected( "true".equals( appConfig.getProperty( SlipstreamConfig.USE_DEFAULT_UI, "false" ) ) );
editorPanel.getBoolean( REMEMBER_GEOMETRY ).setSelected( "true".equals( appConfig.getProperty( SlipstreamConfig.REMEMBER_GEOMETRY, "true" ) ) );
editorPanel.getInt( UPDATE_CATALOG ).setText( Integer.toString( appConfig.getPropertyAsInt( SlipstreamConfig.UPDATE_CATALOG, 0 ) ) );
editorPanel.getInt( UPDATE_APP ).setText( Integer.toString( appConfig.getPropertyAsInt( SlipstreamConfig.UPDATE_APP, 0 ) ) );
@ -94,6 +99,11 @@ public class SlipstreamConfigDialog extends JFrame implements ActionListener {
ftlDatsPathField.setPreferredSize( new Dimension( 150, ftlDatsPathField.getPreferredSize().height ) );
editorPanel.getChooser( FTL_DATS_PATH ).getButton().addActionListener( this );
JTextField steamExePathField = editorPanel.getChooser( STEAM_EXE_PATH ).getTextField();
steamExePathField.setText( appConfig.getProperty( SlipstreamConfig.STEAM_EXE_PATH, "" ) );
steamExePathField.setPreferredSize( new Dimension( 150, steamExePathField.getPreferredSize().height ) );
editorPanel.getChooser( STEAM_EXE_PATH ).getButton().addActionListener( this );
JPanel ctrlPanel = new JPanel();
ctrlPanel.setLayout( new BoxLayout( ctrlPanel, BoxLayout.X_AXIS ) );
ctrlPanel.setBorder( BorderFactory.createEmptyBorder( 10, 0, 10, 0 ) );
@ -165,6 +175,11 @@ public class SlipstreamConfigDialog extends JFrame implements ActionListener {
appConfig.setProperty( SlipstreamConfig.FTL_DATS_PATH, tmp );
}
tmp = editorPanel.getChooser( STEAM_EXE_PATH ).getTextField().getText();
if ( tmp.length() > 0 && new File( tmp ).exists() ) {
appConfig.setProperty( SlipstreamConfig.STEAM_EXE_PATH, tmp );
}
this.setVisible( false );
this.dispose();
}
@ -174,5 +189,23 @@ public class SlipstreamConfigDialog extends JFrame implements ActionListener {
editorPanel.getChooser( FTL_DATS_PATH ).getTextField().setText( datsDir.getAbsolutePath() );
}
}
else if ( source == editorPanel.getChooser( STEAM_EXE_PATH ).getButton() ) {
String currentPath = editorPanel.getChooser( STEAM_EXE_PATH ).getTextField().getText();
JFileChooser steamExeChooser = new JFileChooser();
steamExeChooser.setDialogTitle( "Find Steam.exe or steam or Steam.app" );
steamExeChooser.setFileHidingEnabled( false );
steamExeChooser.setMultiSelectionEnabled( false );
if ( currentPath.length() > 0 ) {
steamExeChooser.setCurrentDirectory( new File( currentPath ) );
}
if ( steamExeChooser.showOpenDialog( null ) == JFileChooser.APPROVE_OPTION ) {
File steamExeFile = steamExeChooser.getSelectedFile();
if ( steamExeFile.exists() ) {
editorPanel.getChooser( STEAM_EXE_PATH ).getTextField().setText( steamExeFile.getAbsolutePath() );
}
}
}
}
}