____tbvns____ 6 сар өмнө
parent
commit
7b1d58787a

+ 2 - 2
GD4J/pom.xml

@@ -12,8 +12,8 @@
     <artifactId>GD4J</artifactId>
 
     <properties>
-        <maven.compiler.source>22</maven.compiler.source>
-        <maven.compiler.target>22</maven.compiler.target>
+        <maven.compiler.source>21</maven.compiler.source>
+        <maven.compiler.target>21</maven.compiler.target>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
 

+ 109 - 0
GD4J/src/main/java/xyz/tbvns/DataManager.java

@@ -0,0 +1,109 @@
+package xyz.tbvns;
+
+import org.apache.commons.io.FileUtils;
+import xyz.tbvns.Objects.Level;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+
+public class DataManager {
+    public static List<String> decodeLocalLevel() throws IOException {
+        List<String> data = new ArrayList<>();
+        File geometryDash = new File(FilesLocations.getGeometryDashFolder());
+        String[] files = geometryDash.list();
+        for (String file : files) {
+            if (file.contains("CCLocalLevels") && file.endsWith(".dat")) {
+                File fileToDecode = new File(geometryDash.getPath() + "/" + file);
+                data.add(new String(Decoder.decodeFile(fileToDecode, 11)));
+            }
+        }
+        return data;
+    }
+
+    private static String rawData;
+    public static List<Level> getLevels() throws IOException {
+        List<Level> levelList = new ArrayList<>();
+        rawData = XmlUtils.beautify(decodeLocalLevel().get(0));
+        String[] lineData = rawData.split("\n");
+        Level level = new Level();
+        int endTime = 0;
+        for (int i = 0; i < lineData.length; i++) {
+            String line = lineData[i];
+            if (line.startsWith("<k>k_") && line.endsWith("</k>")) {
+                level = new Level();
+            } else
+            if (line.equals("<k>k2</k>")) {
+                level.setName(lineData[i+1].replace("<s>", "").replace("</s>", ""));
+                level.setNameLine(i+1);
+            } else
+            if (line.equals("<k>k3</k>")) {
+                level.setDescription(new String(Base64.getUrlDecoder().decode(lineData[i+1].replace("<s>", "").replace("</s>", ""))));
+                level.setDescriptionLine(i+1);
+            } else
+            if (line.equals("<k>k4</k>")) {
+                level.setEncodedLevelString(lineData[i+1].replace("<s>", "").replace("</s>", ""));
+                level.setEncodedLevelStringLine(i+1);
+            } else
+            if (line.equals("<k>k5</k>")) {
+                level.setCreator(lineData[i+1].replace("<s>", "").replace("</s>", ""));
+                level.setCreatorLine(i+1);
+            } else
+            if (line.equals("</d>")) {
+                endTime++;
+                if (endTime % 2 == 0) {
+                    levelList.add(level);
+                }
+            } else
+            if (line.equals("<k>LLM_02</k>")) {
+                break;
+            }
+        }
+        return levelList;
+    }
+
+    public static void saveLevel(Level level) throws IOException {
+        String[] data = rawData.split("\n");
+        System.out.println(level.getEncodedLevelStringLine() + " " + level.getCreatorLine() + " " + level.getNameLine() + " " + level.getDescriptionLine());
+        data[level.getEncodedLevelStringLine()] = "<s>" + level.getEncodedLevelString() + "</s>";
+        data[level.getNameLine()] = "<s>" + level.getName() + "</s>";
+        data[level.getCreatorLine()] = "<s>" + level.getCreator() + "</s>";
+        if (level.getDescriptionLine() != 0) {
+            data[level.getDescriptionLine()] = "<s>" + new String(Base64.getUrlEncoder().encode(level.getDescription().getBytes())) + "</s>";
+        }
+
+        StringBuilder decodedData = new StringBuilder();
+        for (String s : data) {
+            decodedData.append(s);
+        }
+        File file = new File(FilesLocations.getGeometryDashFolder() + "/CCLocalLevels.dat");
+        FileUtils.writeStringToFile(file, decodedData.toString().replace("\n", ""), StandardCharsets.UTF_8, false);
+        Decoder.encodeFile(file, 11);
+    }
+
+    public static void saveLevel(Level level, UIProgressBar progressBar, float factor) throws IOException {
+        progressBar.updateBar(progressBar.getProgressBar().getProgress() + 0.2 * factor);
+        String[] data = rawData.split("\n");
+        data[level.getEncodedLevelStringLine()] = "<s>" + Decoder.encodeLevel(level.getEncodedLevelString()) + "</s>";
+        data[level.getNameLine()] = "<s>" + level.getName() + "</s>";
+        data[level.getCreatorLine()] = "<s>" + level.getCreator() + "</s>";
+        if (level.getDescriptionLine() != 0) {
+            data[level.getDescriptionLine()] = "<s>" + new String(Base64.getUrlEncoder().encode(level.getDescription().getBytes())) + "</s>";
+        }
+        progressBar.updateBar(progressBar.getProgressBar().getProgress() + 0.2 * factor);
+
+        StringBuilder decodedData = new StringBuilder();
+        for (String s : data) {
+            decodedData.append(s);
+        }
+        progressBar.updateBar(progressBar.getProgressBar().getProgress() + 0.2 * factor);
+
+        File file = new File(FilesLocations.getGeometryDashFolder() + "/CCLocalLevels.dat");
+        FileUtils.writeStringToFile(file, decodedData.toString().replace("\n", ""), StandardCharsets.UTF_8, false);
+        progressBar.updateBar(progressBar.getProgressBar().getProgress() + 0.2 * factor);
+        Decoder.encodeFile(file, 11);
+    }
+}

+ 104 - 0
GD4J/src/main/java/xyz/tbvns/Decoder.java

@@ -0,0 +1,104 @@
+package xyz.tbvns;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.FileUtils;
+import xyz.tbvns.Objects.Level;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+public class Decoder {
+
+    public static final String defaultLevelSettings = "kA13,0,kA15,0,kA16,0,kA14,,kA6,0,kA7,0,kA25,0,kA17,0,kA18,0,kS39,0,kA2,0,kA3,0,kA8,0,kA4,0,kA9,0,kA10,0,kA22,1,kA23,0,kA24,0,kA27,1,kA40,1,kA41,1,kA42,1,kA28,0,kA29,0,kA31,1,kA32,1,kA36,0,kA43,0,kA44,0,kA45,1,kA33,1,kA34,1,kA35,0,kA37,1,kA38,1,kA39,1,kA19,0,kA26,0,kA20,0,kA21,0,kA11,0;";
+
+    public static String decodeLevel(String levelStr) throws IOException {
+        byte[] levelData = Base64.decodeBase64(levelStr.getBytes(StandardCharsets.UTF_8));
+        ByteArrayInputStream inputStream = new ByteArrayInputStream(levelData);
+        GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream);
+
+        return new String(gzipInputStream.readAllBytes());
+    }
+
+    public static String encodeLevel(String levelStr) throws IOException {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
+        gzipOutputStream.write(levelStr.getBytes(StandardCharsets.UTF_8));
+        gzipOutputStream.flush();
+        gzipOutputStream.finish();
+        gzipOutputStream.close();
+
+        byte[] levelData = java.util.Base64.getUrlEncoder().encode(outputStream.toByteArray());
+
+        return new String(levelData);
+    }
+
+    public static String encodeLevel(String levelStr, UIProgressBar progressBar, float factor) throws IOException {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
+        gzipOutputStream.write(levelStr.getBytes(StandardCharsets.UTF_8));
+        gzipOutputStream.flush();
+        gzipOutputStream.finish();
+        gzipOutputStream.close();
+        progressBar.updateBar(progressBar.getProgressBar().getProgress() + 0.5 * factor);
+
+        byte[] levelData = java.util.Base64.getUrlEncoder().encode(outputStream.toByteArray());
+        progressBar.updateBar(progressBar.getProgressBar().getProgress() + 0.5 * factor);
+
+        return new String(levelData);
+    }
+
+    public static byte[] decodeFile(File file, int key) throws IOException {
+
+        byte[] levelData = xor(FileUtils.readFileToByteArray(file), key);
+        levelData = Base64.decodeBase64(levelData);
+
+        ByteArrayInputStream inputStream = new ByteArrayInputStream(levelData);
+        GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream);
+
+        return gzipInputStream.readAllBytes();
+    }
+
+    public static byte[] encodeFile(File file, int key) throws IOException {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
+        gzipOutputStream.write(FileUtils.readFileToByteArray(file));
+        gzipOutputStream.flush();
+        gzipOutputStream.finish();
+        gzipOutputStream.close();
+
+        byte[] fileData = java.util.Base64.getUrlEncoder().encode(outputStream.toByteArray());
+        fileData = xor(fileData, key);
+        FileUtils.writeByteArrayToFile(file, fileData, false);
+
+        return fileData;
+    }
+
+    public static void exportGMD(String name, String data, String path) throws IOException, URISyntaxException {
+        String template = new String(Decoder.class.getResourceAsStream("/template.gmd").readAllBytes());
+        template = template.replace("%name%", name).replace("%levelData%", encodeLevel(data));
+        FileUtils.writeStringToFile(new File(path), template, StandardCharsets.UTF_8);
+    }
+
+    public static void exportGMD(Level level, String path, UIProgressBar progressBar, float factor) throws IOException, URISyntaxException {
+        String template = new String(Decoder.class.getResourceAsStream("/template.gmd").readAllBytes());
+        progressBar.updateBar(progressBar.getProgressBar().getProgress() + 0.2 * factor);
+        template = template.replace("%name%", level.getName()).replace("%levelData%", encodeLevel(level.getEncodedLevelString(), progressBar, factor*0.8f));
+        FileUtils.writeStringToFile(new File(path), template, StandardCharsets.UTF_8);
+        progressBar.updateBar(progressBar.getProgressBar().getProgress() + 0.2 * factor);
+    }
+
+    public static byte[] xor(byte[] in, int key) {
+        byte[] out = new byte[in.length];
+        for (int i = 0; i < in.length; i++) {
+            out[i] = (byte)(in[i] ^ key);
+        }
+        return out;
+    }
+
+}

+ 28 - 0
GD4J/src/main/java/xyz/tbvns/FilesLocations.java

@@ -0,0 +1,28 @@
+package xyz.tbvns;
+
+import lombok.extern.slf4j.Slf4j;
+import oshi.SystemInfo;
+import oshi.software.os.OperatingSystem;
+import oshi.software.os.linux.LinuxOperatingSystem;
+import oshi.software.os.mac.MacOperatingSystem;
+import oshi.software.os.windows.WindowsOperatingSystem;
+
+@Slf4j
+public class FilesLocations {
+    public static final String windowGeometryDashFolder = System.getenv("LOCALAPPDATA") + "/GeometryDash/";
+    public static final String linuxGeometryDashFolder = System.getProperty("user.home") + "/.local/share/Steam/steamapps/compatdata/322170/pfx/drive_c/users/steamuser/AppData/Local/GeometryDash/";
+    public static final String macGeometryDashFolder = System.getProperty("user.home") + "/Library/Application Support/GeometryDash";
+
+    public static String getGeometryDashFolder() {
+        SystemInfo si = new SystemInfo();
+        OperatingSystem os = si.getOperatingSystem();
+        if (os instanceof WindowsOperatingSystem) {
+            return windowGeometryDashFolder;
+        } else if (os instanceof LinuxOperatingSystem) {
+            return linuxGeometryDashFolder;
+        } else if (os instanceof MacOperatingSystem) {
+            return macGeometryDashFolder;
+        }
+        throw new RuntimeException("Unsupported operating system !");
+    }
+}

+ 0 - 17
GD4J/src/main/java/xyz/tbvns/Main.java

@@ -1,17 +0,0 @@
-package xyz.tbvns;
-
-//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
-// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
-public class Main {
-    public static void main(String[] args) {
-        //TIP Press <shortcut actionId="ShowIntentionActions"/> with your caret at the highlighted text
-        // to see how IntelliJ IDEA suggests fixing it.
-        System.out.printf("Hello and welcome!");
-
-        for (int i = 1; i <= 5; i++) {
-            //TIP Press <shortcut actionId="Debug"/> to start debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
-            // for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.
-            System.out.println("i = " + i);
-        }
-    }
-}

+ 15 - 0
GD4J/src/main/java/xyz/tbvns/Objects/Level.java

@@ -0,0 +1,15 @@
+package xyz.tbvns.Objects;
+
+import lombok.Data;
+
+@Data
+public class Level {
+    private String name;
+    private String creator;
+    private String description;
+    private String encodedLevelString;
+    private int nameLine;
+    private int creatorLine;
+    private int descriptionLine;
+    private int encodedLevelStringLine;
+}

+ 65 - 0
GD4J/src/main/java/xyz/tbvns/UIProgressBar.java

@@ -0,0 +1,65 @@
+package xyz.tbvns;
+
+import javafx.application.Platform;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.scene.control.ProgressBar;
+import javafx.scene.layout.FlowPane;
+import javafx.stage.Stage;
+import lombok.Getter;
+
+public class UIProgressBar {
+    @Getter private ProgressBar progressBar;
+    @Getter private Stage stage;
+    public void open(String title) {
+        Platform.runLater(() -> {
+            openNow(title);
+        });
+    }
+
+    public void openNow(String title) {
+        stage = new Stage();
+        stage.setWidth(200);
+        stage.setHeight(80);
+        stage.setAlwaysOnTop(true);
+        stage.setResizable(false);
+
+        FlowPane pane = new FlowPane();
+        pane.setPrefWidth(stage.getWidth());
+        pane.setPrefHeight(stage.getHeight());
+        pane.setAlignment(Pos.CENTER);
+
+        Label label = new Label(title);
+        label.setPrefWidth(pane.getPrefWidth() - 20);
+        pane.getChildren().add(label);
+
+        progressBar = new ProgressBar();
+        progressBar.setProgress(0);
+        progressBar.setPrefWidth(pane.getPrefWidth() - 20);
+        pane.getChildren().add(progressBar);
+
+        Scene scene = new Scene(pane);
+        stage.setScene(scene);
+        stage.show();
+    }
+
+    public void updateBar(double value) {
+        Platform.runLater(() -> {
+            progressBar.setProgress(value);
+        });
+    }
+
+    public void close() {
+        new Thread(() -> {
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+            Platform.runLater(() -> {
+                stage.close();
+            });
+        }).start();
+    }
+}

+ 28 - 0
GD4J/src/main/java/xyz/tbvns/XmlUtils.java

@@ -0,0 +1,28 @@
+package xyz.tbvns;
+
+public class XmlUtils {
+    public static String beautify(String xml) {
+        StringBuilder result = new StringBuilder();
+        for (char c : xml.toCharArray()) {
+            if (c == '<') {
+                result.append("\n").append(c);
+            } else if (c == '>') {
+                result.append(c).append("\n");
+            } else {
+                result.append(c);
+            }
+        }
+        String newResult = result.toString();
+        char[] data = newResult.toCharArray();
+        StringBuilder finalResult = new StringBuilder();
+        for (int i = 1; i < newResult.length() - 1; i++) {
+            char c1 = data[i];
+            char c2 = data[i+1];
+            if (!(c1 == '\n' && c2 != '\n')) {
+                finalResult.append(c1);
+            }
+        }
+
+        return finalResult.toString();
+    }
+}

+ 52 - 2
PowerGDEditor/pom.xml

@@ -12,9 +12,59 @@
     <artifactId>PowerGDEditor</artifactId>
 
     <properties>
-        <maven.compiler.source>22</maven.compiler.source>
-        <maven.compiler.target>22</maven.compiler.target>
+        <maven.compiler.source>21</maven.compiler.source>
+        <maven.compiler.target>21</maven.compiler.target>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
 
+    <dependencies>
+        <dependency>
+            <groupId>com.badlogicgames.gdx</groupId>
+            <artifactId>gdx</artifactId>
+            <version>1.12.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.badlogicgames.gdx</groupId>
+            <artifactId>gdx-backend-lwjgl3</artifactId>
+            <version>1.12.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.badlogicgames.gdx</groupId>
+            <artifactId>gdx-platform</artifactId>
+            <version>1.12.1</version>
+            <classifier>natives-desktop</classifier>
+        </dependency>
+        <dependency>
+            <groupId>com.badlogicgames.gdx</groupId>
+            <artifactId>gdx-backend-headless</artifactId>
+            <version>1.12.1</version>
+        </dependency>
+        <!-- ImGUI -->
+        <dependency>
+            <groupId>io.github.spair</groupId>
+            <artifactId>imgui-java-binding</artifactId>
+            <version>1.86.11</version>
+        </dependency>
+        <dependency>
+            <groupId>io.github.spair</groupId>
+            <artifactId>imgui-java-lwjgl3</artifactId>
+            <version>1.86.11</version>
+        </dependency>
+        <dependency>
+            <groupId>io.github.spair</groupId>
+            <artifactId>imgui-java-natives-linux</artifactId>
+            <version>1.86.11</version>
+        </dependency>
+        <dependency>
+            <groupId>io.github.spair</groupId>
+            <artifactId>imgui-java-natives-windows</artifactId>
+            <version>1.86.11</version>
+        </dependency>
+        <dependency>
+            <groupId>io.github.spair</groupId>
+            <artifactId>imgui-java-natives-macos</artifactId>
+            <version>1.86.11</version>
+        </dependency>
+    </dependencies>
+
 </project>

+ 36 - 0
PowerGDEditor/src/main/java/xyz/tbvns/LWJGL3/Lwjgl3Launcher.java

@@ -0,0 +1,36 @@
+package xyz.tbvns.LWJGL3;
+
+import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
+import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration;
+import xyz.tbvns.Main;
+
+/** Launches the desktop (LWJGL3) application. */
+public class Lwjgl3Launcher {
+    public static void main(String[] args) {
+        if (StartupHelper.startNewJvmIfRequired()) return; // This handles macOS support and helps on Windows.
+        createApplication();
+    }
+
+    private static Lwjgl3Application createApplication() {
+        return new Lwjgl3Application(new Main(), getDefaultConfiguration());
+    }
+
+    private static Lwjgl3ApplicationConfiguration getDefaultConfiguration() {
+        Lwjgl3ApplicationConfiguration configuration = new Lwjgl3ApplicationConfiguration();
+        configuration.setTitle("MiningGame");
+        //// Vsync limits the frames per second to what your hardware can display, and helps eliminate
+        //// screen tearing. This setting doesn't always work on Linux, so the line after is a safeguard.
+        configuration.useVsync(true);
+        //// Limits FPS to the refresh rate of the currently active monitor, plus 1 to try to match fractional
+        //// refresh rates. The Vsync setting above should limit the actual FPS to match the monitor.
+        configuration.setForegroundFPS(Lwjgl3ApplicationConfiguration.getDisplayMode().refreshRate + 1);
+        //// If you remove the above line and set Vsync to false, you can get unlimited FPS, which can be
+        //// useful for testing performance, but can also be very stressful to some hardware.
+        //// You may also need to configure GPU drivers to fully disable Vsync; this can cause screen tearing.
+        configuration.setWindowedMode(640, 480);
+        //// You can change these files; they are in lwjgl3/src/main/resources/ .
+        //TODO: vvvv Re add that vvvv
+        //configuration.setWindowIcon("libgdx128.png", "libgdx64.png", "libgdx32.png", "libgdx16.png");
+        return configuration;
+    }
+}

+ 179 - 0
PowerGDEditor/src/main/java/xyz/tbvns/LWJGL3/StartupHelper.java

@@ -0,0 +1,179 @@
+/*
+ * Copyright 2020 damios
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+//Note, the above license and copyright applies to this file only.
+
+package xyz.tbvns.LWJGL3;
+
+import org.lwjgl.system.macosx.LibC;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStreamReader;
+import java.lang.management.ManagementFactory;
+import java.util.ArrayList;
+
+/**
+ * Adds some utilities to ensure that the JVM was started with the
+ * {@code -XstartOnFirstThread} argument, which is required on macOS for LWJGL 3
+ * to function. Also helps on Windows when users have names with characters from
+ * outside the Latin alphabet, a common cause of startup crashes.
+ * <br>
+ * <a href="https://jvm-gaming.org/t/starting-jvm-on-mac-with-xstartonfirstthread-programmatically/57547">Based on this java-gaming.org post by kappa</a>
+ * @author damios
+ */
+public class StartupHelper {
+
+    private static final String JVM_RESTARTED_ARG = "jvmIsRestarted";
+
+    private StartupHelper() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Starts a new JVM if the application was started on macOS without the
+     * {@code -XstartOnFirstThread} argument. This also includes some code for
+     * Windows, for the case where the user's home directory includes certain
+     * non-Latin-alphabet characters (without this code, most LWJGL3 apps fail
+     * immediately for those users). Returns whether a new JVM was started and
+     * thus no code should be executed.
+     * <p>
+     * <u>Usage:</u>
+     *
+     * <pre><code>
+     * public static void main(String... args) {
+     * 	if (StartupHelper.startNewJvmIfRequired(true)) return; // This handles macOS support and helps on Windows.
+     * 	// after this is the actual main method code
+     * }
+     * </code></pre>
+     *
+     * @param redirectOutput
+     *            whether the output of the new JVM should be rerouted to the
+     *            old JVM, so it can be accessed in the same place; keeps the
+     *            old JVM running if enabled
+     * @return whether a new JVM was started and thus no code should be executed
+     *         in this one
+     */
+    public static boolean startNewJvmIfRequired(boolean redirectOutput) {
+        String osName = System.getProperty("os.name").toLowerCase();
+        if (!osName.contains("mac")) {
+            if (osName.contains("windows")) {
+// Here, we are trying to work around an issue with how LWJGL3 loads its extracted .dll files.
+// By default, LWJGL3 extracts to the directory specified by "java.io.tmpdir", which is usually the user's home.
+// If the user's name has non-ASCII (or some non-alphanumeric) characters in it, that would fail.
+// By extracting to the relevant "ProgramData" folder, which is usually "C:\ProgramData", we avoid this.
+                System.setProperty("java.io.tmpdir", System.getenv("ProgramData") + "/libGDX-temp");
+            }
+            return false;
+        }
+
+        // There is no need for -XstartOnFirstThread on Graal native image
+        if (!System.getProperty("org.graalvm.nativeimage.imagecode", "").isEmpty()) {
+            return false;
+        }
+
+        long pid = LibC.getpid();
+
+        // check whether -XstartOnFirstThread is enabled
+        if ("1".equals(System.getenv("JAVA_STARTED_ON_FIRST_THREAD_" + pid))) {
+            return false;
+        }
+
+        // check whether the JVM was previously restarted
+        // avoids looping, but most certainly leads to a crash
+        if ("true".equals(System.getProperty(JVM_RESTARTED_ARG))) {
+            System.err.println(
+                    "There was a problem evaluating whether the JVM was started with the -XstartOnFirstThread argument.");
+            return false;
+        }
+
+        // Restart the JVM with -XstartOnFirstThread
+        ArrayList<String> jvmArgs = new ArrayList<>();
+        String separator = System.getProperty("file.separator");
+        // The following line is used assuming you target Java 8, the minimum for LWJGL3.
+        String javaExecPath = System.getProperty("java.home") + separator + "bin" + separator + "java";
+        // If targeting Java 9 or higher, you could use the following instead of the above line:
+        //String javaExecPath = ProcessHandle.current().info().command().orElseThrow();
+
+        if (!(new File(javaExecPath)).exists()) {
+            System.err.println(
+                    "A Java installation could not be found. If you are distributing this app with a bundled JRE, be sure to set the -XstartOnFirstThread argument manually!");
+            return false;
+        }
+
+        jvmArgs.add(javaExecPath);
+        jvmArgs.add("-XstartOnFirstThread");
+        jvmArgs.add("-D" + JVM_RESTARTED_ARG + "=true");
+        jvmArgs.addAll(ManagementFactory.getRuntimeMXBean().getInputArguments());
+        jvmArgs.add("-cp");
+        jvmArgs.add(System.getProperty("java.class.path"));
+        String mainClass = System.getenv("JAVA_MAIN_CLASS_" + pid);
+        if (mainClass == null) {
+            StackTraceElement[] trace = Thread.currentThread().getStackTrace();
+            if (trace.length > 0) {
+                mainClass = trace[trace.length - 1].getClassName();
+            } else {
+                System.err.println("The main class could not be determined.");
+                return false;
+            }
+        }
+        jvmArgs.add(mainClass);
+
+        try {
+            if (!redirectOutput) {
+                ProcessBuilder processBuilder = new ProcessBuilder(jvmArgs);
+                processBuilder.start();
+            } else {
+                Process process = (new ProcessBuilder(jvmArgs))
+                        .redirectErrorStream(true).start();
+                BufferedReader processOutput = new BufferedReader(
+                        new InputStreamReader(process.getInputStream()));
+                String line;
+
+                while ((line = processOutput.readLine()) != null) {
+                    System.out.println(line);
+                }
+
+                process.waitFor();
+            }
+        } catch (Exception e) {
+            System.err.println("There was a problem restarting the JVM");
+            e.printStackTrace();
+        }
+
+        return true;
+    }
+
+    /**
+     * Starts a new JVM if the application was started on macOS without the
+     * {@code -XstartOnFirstThread} argument. Returns whether a new JVM was
+     * started and thus no code should be executed. Redirects the output of the
+     * new JVM to the old one.
+     * <p>
+     * <u>Usage:</u>
+     *
+     * <pre>
+     * public static void main(String... args) {
+     * 	if (StartupHelper.startNewJvmIfRequired()) return; // This handles macOS support and helps on Windows.
+     * 	// the actual main method code
+     * }
+     * </pre>
+     *
+     * @return whether a new JVM was started and thus no code should be executed
+     *         in this one
+     */
+    public static boolean startNewJvmIfRequired() {
+        return startNewJvmIfRequired(true);
+    }
+}

+ 44 - 13
PowerGDEditor/src/main/java/xyz/tbvns/Main.java

@@ -1,17 +1,48 @@
 package xyz.tbvns;
 
-//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
-// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
-public class Main {
-    public static void main(String[] args) {
-        //TIP Press <shortcut actionId="ShowIntentionActions"/> with your caret at the highlighted text
-        // to see how IntelliJ IDEA suggests fixing it.
-        System.out.printf("Hello and welcome!");
+import com.badlogic.gdx.ApplicationAdapter;
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.InputProcessor;
+import com.badlogic.gdx.backends.headless.HeadlessFiles;
+import com.badlogic.gdx.backends.headless.HeadlessNativesLoader;
+import com.badlogic.gdx.backends.headless.mock.graphics.MockGraphics;
+import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Graphics;
+import com.badlogic.gdx.graphics.GL20;
+import com.badlogic.gdx.graphics.Texture;
+import com.badlogic.gdx.graphics.g2d.SpriteBatch;
+import com.badlogic.gdx.utils.ScreenUtils;
+import imgui.ImGui;
+import imgui.ImGuiIO;
+import imgui.gl3.ImGuiImplGl3;
+import imgui.glfw.ImGuiImplGlfw;
+import xyz.tbvns.ui.Ui;
 
-        for (int i = 1; i <= 5; i++) {
-            //TIP Press <shortcut actionId="Debug"/> to start debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
-            // for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.
-            System.out.println("i = " + i);
-        }
+/** {@link com.badlogic.gdx.ApplicationListener} implementation shared by all platforms. */
+public class Main extends ApplicationAdapter {
+    private SpriteBatch batch;
+    private Texture image;
+
+    @Override
+    public void create() {
+        Ui.initImGui();
+        batch = new SpriteBatch();
+        image = new Texture("libgdx.png");
+    }
+
+    @Override
+    public void render() {
+        ScreenUtils.clear(0.15f, 0.15f, 0.2f, 1f);
+        batch.begin();
+        batch.draw(image, 140, 210);
+        batch.end();
+
+        Ui.render();
+    }
+
+    @Override
+    public void dispose() {
+        batch.dispose();
+        image.dispose();
+        Ui.dispose();
     }
-}
+}

+ 18 - 0
PowerGDEditor/src/main/java/xyz/tbvns/Utils.java

@@ -0,0 +1,18 @@
+package xyz.tbvns;
+
+import com.badlogic.gdx.graphics.Pixmap;
+
+import java.awt.image.BufferedImage;
+
+public class Utils {
+    private BufferedImage pixmapToBufferedImage(Pixmap img) {
+        int w = img.getWidth(), h = img.getHeight();
+
+        int[] pixels = new int[w * h];
+        img.getPixels().asIntBuffer().get(pixels);
+
+        BufferedImage out = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
+        out.setRGB(0, 0, w, h, pixels, 0, w);
+        return out;
+    }
+}

+ 78 - 0
PowerGDEditor/src/main/java/xyz/tbvns/ui/Ui.java

@@ -0,0 +1,78 @@
+package xyz.tbvns.ui;
+
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.InputProcessor;
+import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Graphics;
+import imgui.ImGui;
+import imgui.ImGuiIO;
+import imgui.flag.ImGuiWindowFlags;
+import imgui.gl3.ImGuiImplGl3;
+import imgui.glfw.ImGuiImplGlfw;
+import xyz.tbvns.Main;
+
+public class Ui {
+    static private ImGuiImplGlfw imGuiGlfw;
+    static private ImGuiImplGl3 imGuiGl3;
+    private static InputProcessor tmpProcessor;
+
+    public static void render() {
+        startImGui();
+        ImGui.beginMainMenuBar();
+        ImGui.button("Button world !");
+
+        ImGui.endMainMenuBar();
+
+        ImGui.begin("Menu", ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoMove);
+        ImGui.setWindowSize(200, Gdx.graphics.getHeight() - 19 - 200);
+        ImGui.setWindowPos(0, 19);
+        ImGui.end();
+
+        ImGui.begin("TimeMenu", ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoMove);
+        ImGui.setWindowSize(Gdx.graphics.getWidth(), 201);
+        ImGui.setWindowPos(0, Gdx.graphics.getHeight() - 201);
+        ImGui.end();
+        endImGui();
+    }
+
+    public static void initImGui() {
+        imGuiGlfw = new ImGuiImplGlfw();
+        imGuiGl3 = new ImGuiImplGl3();
+        long windowHandle = ((Lwjgl3Graphics) Gdx.graphics).getWindow().getWindowHandle();
+        ImGui.createContext();
+        ImGuiIO io = ImGui.getIO();
+        io.setIniFilename(null);
+        io.getFonts().addFontDefault();
+        io.getFonts().build();
+        imGuiGlfw.init(windowHandle, true);
+        imGuiGl3.init("#version 150");
+    }
+
+    public static void startImGui() {
+        if (tmpProcessor != null) { // Restore the input processor after ImGui caught all inputs, see #end()
+            Gdx.input.setInputProcessor(tmpProcessor);
+            tmpProcessor = null;
+        }
+
+        imGuiGlfw.newFrame();
+        ImGui.newFrame();
+    }
+
+    public static void endImGui() {
+        ImGui.render();
+        imGuiGl3.renderDrawData(ImGui.getDrawData());
+
+        // If ImGui wants to capture the input, disable libGDX's input processor
+        if (ImGui.getIO().getWantCaptureKeyboard() || ImGui.getIO().getWantCaptureMouse()) {
+            tmpProcessor = Gdx.input.getInputProcessor();
+            Gdx.input.setInputProcessor(null);
+        }
+    }
+
+    public static void dispose() {
+        imGuiGl3.dispose();
+        imGuiGl3 = null;
+        imGuiGlfw.dispose();
+        imGuiGlfw = null;
+        ImGui.destroyContext();
+    }
+}

BIN
PowerGDEditor/src/main/resources/libgdx.png