diff --git a/README.md b/README.md
index bdaef1cd..aac8f463 100644
--- a/README.md
+++ b/README.md
@@ -40,22 +40,19 @@ the song menu.
## Building
opsu! is distributed as both a Maven and Gradle project.
+### Maven
+Maven builds are built to the `target` directory.
+* To run the project, execute the Maven goal `compile`.
+* To create a single executable JAR file, execute the Maven goal `package -Djar`.
+ This will compile a jar to `target/opsu-${version}.jar` with the libraries,
+ resources and natives packed inside the jar.
+
### Gradle
Gradle builds are built to the `build` directory.
* To run the project, execute the Gradle task `run`.
-* To create a single executable JAR file, execute the Gradle task
- `build`. This will compile a jar to `build/libs/opsu-${version}.jar` with
- the libraries, resources and natives packed inside the jar.
-
-### Maven
-Maven builds are built to the `target` directory.
-* To run the project, execute the Maven goal `compile exec:exec`.
-* To create a single executable JAR file, execute the Maven goal
- `install -Djar`. This will link the LWJGL native libraries using a
- [modified version](https://github.com/itdelatrisu/JarSplicePlus) of
- [JarSplice](http://ninjacave.com/jarsplice), which is included in the
- `tools` directory in both its original and modified forms. The resulting
- file will be located in `target/opsu-${version}-runnable.jar`.
+* To create a single executable JAR file, execute the Gradle task `build`.
+ This will compile a jar to `build/libs/opsu-${version}.jar` with the libraries,
+ resources and natives packed inside the jar.
## Credits
This software was created by Jeffrey Han
diff --git a/build.gradle b/build.gradle
index 22940162..544eee25 100644
--- a/build.gradle
+++ b/build.gradle
@@ -12,6 +12,8 @@ version = '0.10.1'
mainClassName = 'itdelatrisu.opsu.Opsu'
buildDir = new File(rootProject.projectDir, "build/")
+def useXDG = 'false'
+
sourceCompatibility = 1.7
targetCompatibility = 1.7
@@ -43,8 +45,6 @@ dependencies {
compile 'org.apache.commons:commons-compress:1.9'
compile 'org.tukaani:xz:1.5'
compile 'com.github.jponge:lzma-java:1.3'
-
-
}
def nativePlatforms = ['windows', 'linux', 'osx']
@@ -84,7 +84,8 @@ jar {
manifest {
attributes 'Implementation-Title': 'opsu!',
'Implementation-Version': version,
- 'Main-Class': mainClassName
+ 'Main-Class': mainClassName,
+ 'Use-XDG': useXDG
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
@@ -92,6 +93,8 @@ jar {
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
exclude '**/Thumbs.db'
+
+ outputs.upToDateWhen { false }
}
run {
diff --git a/pom.xml b/pom.xml
index ce9bc066..ca910302 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,6 +9,7 @@
${maven.build.timestamp}
yyyy-MM-dd HH:mm
itdelatrisu.opsu.Opsu
+ false
src
@@ -107,7 +108,10 @@
- ${mainClassName}
+
+ ${mainClassName}
+ ${useXDG}
+
false
diff --git a/src/itdelatrisu/opsu/NativeLoader.java b/src/itdelatrisu/opsu/NativeLoader.java
index ee114146..c66ee86c 100644
--- a/src/itdelatrisu/opsu/NativeLoader.java
+++ b/src/itdelatrisu/opsu/NativeLoader.java
@@ -23,9 +23,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.security.CodeSource;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@@ -47,41 +44,32 @@ public class NativeLoader {
if (!NATIVE_DIR.exists())
NATIVE_DIR.mkdir();
- CodeSource src = NativeLoader.class.getProtectionDomain().getCodeSource();
- if (src != null) {
- URI jar = null;
- try {
- jar = src.getLocation().toURI();
- } catch (URISyntaxException e) {
- e.printStackTrace();
- return;
+ JarFile jarFile = Utils.getJarFile();
+ if (jarFile == null)
+ return;
+
+ Enumeration entries = jarFile.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry e = entries.nextElement();
+ if (e == null)
+ break;
+
+ File f = new File(NATIVE_DIR, e.getName());
+ if (isNativeFile(e.getName()) && !e.isDirectory() && e.getName().indexOf('/') == -1 && !f.exists()) {
+ InputStream in = jarFile.getInputStream(jarFile.getEntry(e.getName()));
+ OutputStream out = new FileOutputStream(f);
+
+ byte[] buffer = new byte[65536];
+ int bufferSize;
+ while ((bufferSize = in.read(buffer, 0, buffer.length)) != -1)
+ out.write(buffer, 0, bufferSize);
+
+ in.close();
+ out.close();
}
-
- JarFile jarFile = new JarFile(new File(jar), false);
- Enumeration entries = jarFile.entries();
-
- while (entries.hasMoreElements()) {
- JarEntry e = entries.nextElement();
- if (e == null)
- break;
-
- File f = new File(NATIVE_DIR, e.getName());
- if (isNativeFile(e.getName()) && !e.isDirectory() && e.getName().indexOf('/') == -1 && !f.exists()) {
- InputStream in = jarFile.getInputStream(jarFile.getEntry(e.getName()));
- OutputStream out = new FileOutputStream(f);
-
- byte[] buffer = new byte[65536];
- int bufferSize;
- while ((bufferSize = in.read(buffer, 0, buffer.length)) != -1)
- out.write(buffer, 0, bufferSize);
-
- in.close();
- out.close();
- }
- }
-
- jarFile.close();
}
+
+ jarFile.close();
}
/**
diff --git a/src/itdelatrisu/opsu/Opsu.java b/src/itdelatrisu/opsu/Opsu.java
index 552ff3d2..fc67d143 100644
--- a/src/itdelatrisu/opsu/Opsu.java
+++ b/src/itdelatrisu/opsu/Opsu.java
@@ -18,12 +18,16 @@
package itdelatrisu.opsu;
-import itdelatrisu.opsu.audio.MusicController;
-import itdelatrisu.opsu.db.DBController;
-import itdelatrisu.opsu.downloads.DownloadList;
-import itdelatrisu.opsu.downloads.Updater;
-import itdelatrisu.opsu.states.*;
-import itdelatrisu.opsu.ui.UI;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.Field;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.UnknownHostException;
+
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.SlickException;
@@ -35,11 +39,20 @@ import org.newdawn.slick.util.FileSystemLocation;
import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader;
-import java.io.*;
-import java.lang.reflect.Field;
-import java.net.InetAddress;
-import java.net.ServerSocket;
-import java.net.UnknownHostException;
+import itdelatrisu.opsu.audio.MusicController;
+import itdelatrisu.opsu.db.DBController;
+import itdelatrisu.opsu.downloads.DownloadList;
+import itdelatrisu.opsu.downloads.Updater;
+import itdelatrisu.opsu.states.ButtonMenu;
+import itdelatrisu.opsu.states.DownloadsMenu;
+import itdelatrisu.opsu.states.Game;
+import itdelatrisu.opsu.states.GamePauseMenu;
+import itdelatrisu.opsu.states.GameRanking;
+import itdelatrisu.opsu.states.MainMenu;
+import itdelatrisu.opsu.states.OptionsMenu;
+import itdelatrisu.opsu.states.SongMenu;
+import itdelatrisu.opsu.states.Splash;
+import itdelatrisu.opsu.ui.UI;
/**
* Main class.
@@ -143,7 +156,6 @@ public class Opsu extends StateBasedGame {
Log.warn("Failed to set 'sys_paths' field.", e);
}
-
// set the resource paths
ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/")));
diff --git a/src/itdelatrisu/opsu/Options.java b/src/itdelatrisu/opsu/Options.java
index e9fdbb56..25dddfa2 100644
--- a/src/itdelatrisu/opsu/Options.java
+++ b/src/itdelatrisu/opsu/Options.java
@@ -18,13 +18,6 @@
package itdelatrisu.opsu;
-import itdelatrisu.opsu.audio.MusicController;
-import itdelatrisu.opsu.beatmap.Beatmap;
-import itdelatrisu.opsu.skins.Skin;
-import itdelatrisu.opsu.skins.SkinLoader;
-import itdelatrisu.opsu.ui.Fonts;
-import itdelatrisu.opsu.ui.UI;
-
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
@@ -38,6 +31,9 @@ import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
import org.lwjgl.input.Keyboard;
import org.newdawn.slick.GameContainer;
@@ -48,10 +44,20 @@ import org.newdawn.slick.util.FileSystemLocation;
import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader;
+import itdelatrisu.opsu.audio.MusicController;
+import itdelatrisu.opsu.beatmap.Beatmap;
+import itdelatrisu.opsu.skins.Skin;
+import itdelatrisu.opsu.skins.SkinLoader;
+import itdelatrisu.opsu.ui.Fonts;
+import itdelatrisu.opsu.ui.UI;
+
/**
* Handles all user options.
*/
public class Options {
+ /** Whether to use XDG directories. */
+ private static final boolean USE_XDG = checkXDGFlag();
+
/** The config directory. */
private static final File CONFIG_DIR = getXDGBaseDir("XDG_CONFIG_HOME", ".config");
@@ -120,15 +126,35 @@ public class Options {
/** Port binding. */
private static int port = 49250;
+ /**
+ * Returns whether the XDG flag in the manifest (if any) is set to "true".
+ * @return true if XDG directories are enabled, false otherwise
+ */
+ private static boolean checkXDGFlag() {
+ JarFile jarFile = Utils.getJarFile();
+ if (jarFile == null)
+ return false;
+ try {
+ Manifest manifest = jarFile.getManifest();
+ if (manifest == null)
+ return false;
+ Attributes attributes = manifest.getMainAttributes();
+ String value = attributes.getValue("Use-XDG");
+ return (value != null && value.equalsIgnoreCase("true"));
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
/**
* Returns the directory based on the XDG base directory specification for
- * Unix-like operating systems, only if the system property "XDG" has been defined.
+ * Unix-like operating systems, only if the "XDG" flag is enabled.
* @param env the environment variable to check (XDG_*_*)
* @param fallback the fallback directory relative to ~home
* @return the XDG base directory, or the working directory if unavailable
*/
private static File getXDGBaseDir(String env, String fallback) {
- if (System.getProperty("XDG") == null)
+ if (!USE_XDG)
return new File("./");
String OS = System.getProperty("os.name").toLowerCase();
diff --git a/src/itdelatrisu/opsu/Utils.java b/src/itdelatrisu/opsu/Utils.java
index df07aabb..f1b1e613 100644
--- a/src/itdelatrisu/opsu/Utils.java
+++ b/src/itdelatrisu/opsu/Utils.java
@@ -18,15 +18,6 @@
package itdelatrisu.opsu;
-import itdelatrisu.opsu.audio.SoundController;
-import itdelatrisu.opsu.audio.SoundEffect;
-import itdelatrisu.opsu.beatmap.HitObject;
-import itdelatrisu.opsu.downloads.Download;
-import itdelatrisu.opsu.downloads.DownloadNode;
-import itdelatrisu.opsu.replay.PlaybackSpeed;
-import itdelatrisu.opsu.ui.Fonts;
-import itdelatrisu.opsu.ui.UI;
-
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
@@ -48,6 +39,7 @@ import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Scanner;
+import java.util.jar.JarFile;
import javax.imageio.ImageIO;
@@ -66,6 +58,15 @@ import org.newdawn.slick.util.Log;
import com.sun.jna.platform.FileUtils;
+import itdelatrisu.opsu.audio.SoundController;
+import itdelatrisu.opsu.audio.SoundEffect;
+import itdelatrisu.opsu.beatmap.HitObject;
+import itdelatrisu.opsu.downloads.Download;
+import itdelatrisu.opsu.downloads.DownloadNode;
+import itdelatrisu.opsu.replay.PlaybackSpeed;
+import itdelatrisu.opsu.ui.Fonts;
+import itdelatrisu.opsu.ui.UI;
+
/**
* Contains miscellaneous utilities.
*/
@@ -564,8 +565,25 @@ public class Utils {
return Opsu.class.getResource(String.format("%s.class", Opsu.class.getSimpleName())).toString().startsWith("jar:");
}
+ /**
+ * Returns the JarFile for the application.
+ * @return the JAR file, or null if it could not be determined
+ */
+ public static JarFile getJarFile() {
+ if (!isJarRunning())
+ return null;
+
+ try {
+ return new JarFile(new File(Opsu.class.getProtectionDomain().getCodeSource().getLocation().toURI()), false);
+ } catch (URISyntaxException | IOException e) {
+ Log.error("Could not determine the JAR file.", e);
+ return null;
+ }
+ }
+
/**
* Returns the directory where the application is being run.
+ * @return the directory, or null if it could not be determined
*/
public static File getRunningDirectory() {
try {
diff --git a/src/itdelatrisu/opsu/objects/curves/Curve.java b/src/itdelatrisu/opsu/objects/curves/Curve.java
index 966f7c42..ec8a199d 100644
--- a/src/itdelatrisu/opsu/objects/curves/Curve.java
+++ b/src/itdelatrisu/opsu/objects/curves/Curve.java
@@ -150,6 +150,7 @@ public abstract class Curve {
* @param i the control point index
*/
public float getY(int i) { return (i == 0) ? y : sliderY[i - 1]; }
+
/**
* Discards the slider cache (only used for mmsliders).
*/
diff --git a/tools/JarSplicePlus.jar b/tools/JarSplicePlus.jar
deleted file mode 100644
index b6dacdae..00000000
Binary files a/tools/JarSplicePlus.jar and /dev/null differ