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