فهرست منبع

List levels in GUI

____tbvns____ 6 ماه پیش
والد
کامیت
a5a143882e

+ 38 - 0
.gitignore

@@ -0,0 +1,38 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store

+ 3 - 0
.idea/.gitignore

@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml

+ 7 - 0
.idea/codeStyles/Project.xml

@@ -0,0 +1,7 @@
+<component name="ProjectCodeStyleConfiguration">
+  <code_scheme name="Project" version="173">
+    <ScalaCodeStyleSettings>
+      <option name="MULTILINE_STRING_CLOSING_QUOTES_ON_NEW_LINE" value="true" />
+    </ScalaCodeStyleSettings>
+  </code_scheme>
+</component>

+ 5 - 0
.idea/codeStyles/codeStyleConfig.xml

@@ -0,0 +1,5 @@
+<component name="ProjectCodeStyleConfiguration">
+  <state>
+    <option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
+  </state>
+</component>

+ 11 - 0
.idea/encodings.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Encoding">
+    <file url="file://$PROJECT_DIR$/core/src/main/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/core/src/main/resources" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/ui/src/main/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/ui/src/main/resources" charset="UTF-8" />
+  </component>
+</project>

+ 6 - 0
.idea/git_toolbox_blame.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="GitToolBoxBlameSettings">
+    <option name="version" value="2" />
+  </component>
+</project>

+ 15 - 0
.idea/git_toolbox_prj.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="GitToolBoxProjectSettings">
+    <option name="commitMessageIssueKeyValidationOverride">
+      <BoolValueOverride>
+        <option name="enabled" value="true" />
+      </BoolValueOverride>
+    </option>
+    <option name="commitMessageValidationEnabledOverride">
+      <BoolValueOverride>
+        <option name="enabled" value="true" />
+      </BoolValueOverride>
+    </option>
+  </component>
+</project>

+ 10 - 0
.idea/material_theme_project_new.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="MaterialThemeProjectNewConfig">
+    <option name="metadata">
+      <MTProjectMetadataState>
+        <option name="userId" value="23d1b501:19285a205fe:-7fa7" />
+      </MTProjectMetadataState>
+    </option>
+  </component>
+</project>

+ 14 - 0
.idea/misc.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ExternalStorageConfigurationManager" enabled="true" />
+  <component name="MavenProjectsManager">
+    <option name="originalFiles">
+      <list>
+        <option value="$PROJECT_DIR$/pom.xml" />
+      </list>
+    </option>
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_22" default="true" project-jdk-name="22" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/out" />
+  </component>
+</project>

+ 124 - 0
.idea/uiDesigner.xml

@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Palette2">
+    <group name="Swing">
+      <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
+      </item>
+      <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
+        <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
+        <initial-values>
+          <property name="text" value="Button" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="RadioButton" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="CheckBox" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="Label" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
+          <preferred-size width="-1" height="20" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
+      </item>
+    </group>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>

BIN
GD3D.ico


+ 48 - 0
core/pom.xml

@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>xyz.tbvns</groupId>
+    <artifactId>core</artifactId>
+    <version>1.0-ALPHA</version>
+
+    <properties>
+        <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.github.oshi</groupId>
+            <artifactId>oshi-core</artifactId>
+            <version>6.6.5</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.5.9</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.34</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.17.0</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+            <version>1.17.1</version>
+        </dependency>
+    </dependencies>
+
+</project>

+ 12 - 0
core/src/main/java/module-info.java

@@ -0,0 +1,12 @@
+module core {
+    requires com.github.oshi;
+    requires ch.qos.logback.classic;
+    requires org.slf4j;
+    requires java.logging;
+    requires lombok;
+    requires org.apache.commons.io;
+    requires org.apache.commons.codec;
+
+    exports xyz.tbvns.core;
+    exports xyz.tbvns.core.Objects;
+}

+ 56 - 0
core/src/main/java/xyz/tbvns/core/DataManager.java

@@ -0,0 +1,56 @@
+package xyz.tbvns.core;
+
+import xyz.tbvns.core.Objects.Level;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+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)));
+            }
+        }
+        return data;
+    }
+
+    public static List<Level> getLevels() throws IOException {
+        List<Level> levelList = new ArrayList<>();
+        String rawData = XmlUtils.beautify(decodeLocalLevel().get(1));
+        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>", ""));
+            } else
+            if (line.equals("<k>k4</k>")) {
+                level.setEncodedLevelString(lineData[i+1].replace("<s>", "").replace("</s>", ""));
+            } else
+            if (line.equals("<k>k5</k>")) {
+                level.setCreator(lineData[i+1].replace("<s>", "").replace("</s>", ""));
+            } else
+            if (line.equals("</d>")) {
+                endTime++;
+                if (endTime % 2 == 0) {
+                    levelList.add(level);
+                }
+            } else
+            if (line.equals("<k>LLM_02</k>")) {
+                break;
+            }
+        }
+        return levelList;
+    }
+}

+ 77 - 0
core/src/main/java/xyz/tbvns/core/Decoder.java

@@ -0,0 +1,77 @@
+package xyz.tbvns.core;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.FileUtils;
+
+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());
+        levelData = xor(levelData, 11);
+
+        return new String(levelData);
+    }
+
+    public static byte[] decodeFile(File file) throws IOException {
+
+        byte[] levelData = xor(FileUtils.readFileToByteArray(file), 11);
+        levelData = Base64.decodeBase64(levelData);
+
+        ByteArrayInputStream inputStream = new ByteArrayInputStream(levelData);
+        GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream);
+
+        return gzipInputStream.readAllBytes();
+    }
+
+    public static byte[] encodeFile(File file) throws IOException {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
+        gzipOutputStream.write(FileUtils.readFileToByteArray(file));
+        gzipOutputStream.flush();
+        gzipOutputStream.finish();
+        gzipOutputStream.close();
+
+        return outputStream.toByteArray();
+    }
+
+    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 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
core/src/main/java/xyz/tbvns/core/FilesLocations.java

@@ -0,0 +1,28 @@
+package xyz.tbvns.core;
+
+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 !");
+    }
+}

+ 10 - 0
core/src/main/java/xyz/tbvns/core/Objects/Level.java

@@ -0,0 +1,10 @@
+package xyz.tbvns.core.Objects;
+
+import lombok.Data;
+
+@Data
+public class Level {
+    private String name;
+    private String creator;
+    private String encodedLevelString;
+}

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

@@ -0,0 +1,28 @@
+package xyz.tbvns.core;
+
+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();
+    }
+}

+ 76 - 0
core/src/main/resources/template.gmd

@@ -0,0 +1,76 @@
+<?xml version="1.0"?>
+<plist version="1.0" gjver="2.0">
+<dict>
+<k>kCEK</k>
+<i>4</i>
+<k>k18</k>
+<i>2</i>
+<k>k23</k>
+<i>5</i>
+<k>k2</k>
+<s>%name%</s>
+<k>k4</k>
+<s>%levelData%</s>
+<k>k5</k>
+<s>PowerGD</s>
+<k>k101</k>
+<s>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</s>
+<k>k11</k>
+<i>4</i>
+<k>k13</k>
+<t/>
+<k>k21</k>
+<i>2</i>
+<k>k16</k>
+<i>1</i>
+<k>k80</k>
+<i>844</i>
+<k>k27</k>
+<i>4</i>
+<k>k45</k>
+<i>1167964</i>
+<k>k50</k>
+<i>42</i>
+<k>k47</k>
+<t/>
+<k>k48</k>
+<i>7</i>
+<k>kI1</k>
+<r>220</r>
+<k>kI2</k>
+<r>131.259</r>
+<k>kI3</k>
+<r>0.3</r>
+<k>kI6</k>
+<d>
+<k>0</k>
+<s>0</s>
+<k>1</k>
+<s>0</s>
+<k>2</k>
+<s>0</s>
+<k>3</k>
+<s>0</s>
+<k>4</k>
+<s>0</s>
+<k>5</k>
+<s>0</s>
+<k>6</k>
+<s>0</s>
+<k>7</k>
+<s>0</s>
+<k>8</k>
+<s>0</s>
+<k>9</k>
+<s>0</s>
+<k>10</k>
+<s>0</s>
+<k>11</k>
+<s>0</s>
+<k>12</k>
+<s>0</s>
+<k>13</k>
+<s>0</s>
+</d>
+</dict>
+</plist>

+ 201 - 0
pom.xml

@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>xyz.tbvns</groupId>
+    <artifactId>PowerGD</artifactId>
+    <version>1.0-ALPHA</version>
+    <packaging>pom</packaging>
+
+    <properties>
+        <maven.compiler.source>21</maven.compiler.source>
+        <maven.compiler.target>21</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <organization>
+        <name>Tbvns</name>
+        <url>https://git.tbvns.xyz</url>
+    </organization>
+
+    <inceptionYear>2024</inceptionYear>
+    <name>PowerGD</name>
+    <description>Set of useful tools for geometry dash</description>
+
+    <modules>
+        <module>ui</module>
+        <module>core</module>
+    </modules>
+
+    <repositories>
+        <repository>
+            <id>JCenter</id>
+            <url>https://jcenter.bintray.com/</url>
+        </repository>
+        <repository>
+            <id>jitpack.io</id>
+            <url>https://jitpack.io</url>
+        </repository>
+    </repositories>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-controls</artifactId>
+            <version>21</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-fxml</artifactId>
+            <version>21</version>
+        </dependency>
+        <dependency>
+            <groupId>org.java-websocket</groupId>
+            <artifactId>Java-WebSocket</artifactId>
+            <version>1.5.6</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/javax.vecmath/vecmath -->
+        <dependency>
+            <groupId>org.joml</groupId>
+            <artifactId>joml</artifactId>
+            <version>1.10.5</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/org.locationtech.jts.io/jts-io-common -->
+        <dependency>
+            <groupId>org.locationtech.jts.io</groupId>
+            <artifactId>jts-io-common</artifactId>
+            <version>1.19.0</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/org.locationtech.jts/jts-core -->
+        <dependency>
+            <groupId>org.locationtech.jts</groupId>
+            <artifactId>jts-core</artifactId>
+            <version>1.19.0</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/org.locationtech.jts/jts -->
+        <dependency>
+            <groupId>org.locationtech.jts</groupId>
+            <artifactId>jts</artifactId>
+            <version>1.19.0</version>
+            <type>pom</type>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/org.openjfx/javafx-web -->
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-web</artifactId>
+            <version>23-ea+18</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+            <version>1.17.1</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.17.0</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.jcraft/jzlib -->
+        <dependency>
+            <groupId>com.jcraft</groupId>
+            <artifactId>jzlib</artifactId>
+            <version>1.1.3</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.github.oshi/oshi-core -->
+        <dependency>
+            <groupId>com.github.oshi</groupId>
+            <artifactId>oshi-core</artifactId>
+            <version>6.6.5</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+            <version>1.18.34</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-xml -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-xml</artifactId>
+            <version>2.18.0</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.18.0</version>
+        </dependency>
+    </dependencies>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <configuration>
+                    <descriptorRefs>
+                        <descriptorRef>jar-with-dependencies</descriptorRef>
+                    </descriptorRefs>
+                    <archive>
+                        <manifest>
+                            <mainClass>xyz.tbvns.ui.Main</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>make-assembly</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>com.akathist.maven.plugins.launch4j</groupId>
+                <artifactId>launch4j-maven-plugin</artifactId>
+                <version>2.5.0</version>
+                <executions>
+                    <execution>
+                        <id>l4j-gui</id>
+                        <phase>package</phase>
+                        <goals><goal>launch4j</goal></goals>
+                        <configuration>
+                            <icon>GD3D.ico</icon>
+                            <classPath>
+                                <mainClass>xyz.tbvns.ui.Main</mainClass>
+                            </classPath>
+                            <headerType>gui</headerType>
+                            <outfile>target/PowerGD.exe</outfile>
+                            <jar>./target/PowerGD-1.0-ALPHA-jar-with-dependencies.jar</jar>
+                            <errTitle>PowerGD - Error:</errTitle>
+
+                            <jre>
+                                <minVersion>21</minVersion>
+                            </jre>
+
+                            <versionInfo>
+                                <fileVersion>1.2.3.4</fileVersion>
+                                <txtFileVersion>txt file version?</txtFileVersion>
+                                <fileDescription>a description</fileDescription>
+                                <copyright>my copyright</copyright>
+                                <productVersion>4.3.2.1</productVersion>
+                                <txtProductVersion>txt product version</txtProductVersion>
+                                <productName>GD3D</productName>
+                                <internalName>GD3D</internalName>
+                                <originalFilename>GD3D.exe</originalFilename>
+                            </versionInfo>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 65 - 0
ui/pom.xml

@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>xyz.tbvns</groupId>
+    <artifactId>ui</artifactId>
+    <version>1.0-ALPHA</version>
+
+    <properties>
+        <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>xyz.tbvns</groupId>
+            <artifactId>core</artifactId>
+            <version>1.0-ALPHA</version>
+        </dependency>
+        <dependency>
+            <groupId>xyz.tbvns</groupId>
+            <artifactId>PowerGD</artifactId>
+            <version>1.0-ALPHA</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-reload4j</artifactId>
+            <version>2.1.0-alpha1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>2.1.0-alpha1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.fusesource.jansi</groupId>
+            <artifactId>jansi</artifactId>
+            <version>1.18</version>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>com.jcabi</groupId>
+            <artifactId>jcabi-log</artifactId>
+            <version>LATEST</version>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+            <version>1.18.34</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.5.9</version>
+        </dependency>
+    </dependencies>
+
+</project>

+ 13 - 0
ui/src/main/java/module-info.java

@@ -0,0 +1,13 @@
+module ui {
+    requires core;
+    requires ch.qos.logback.classic;
+    requires org.slf4j;
+    requires java.logging;
+    requires lombok;
+    requires com.github.oshi;
+    requires org.apache.commons.io;
+    requires javafx.controls;
+    requires java.desktop;
+
+    exports xyz.tbvns.ui;
+}

+ 83 - 0
ui/src/main/java/xyz/tbvns/ui/LevelSelectScreen.java

@@ -0,0 +1,83 @@
+package xyz.tbvns.ui;
+
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.layout.*;
+import javafx.scene.paint.Color;
+import javafx.scene.text.Font;
+import lombok.extern.slf4j.Slf4j;
+import xyz.tbvns.core.DataManager;
+import xyz.tbvns.core.Objects.Level;
+
+import java.util.List;
+
+@Slf4j
+public class LevelSelectScreen {
+    public static Label label = new Label("Select the level to edit:");
+    public static Pane pane() {
+        FlowPane pane = new FlowPane();
+        pane.setAlignment(Pos.TOP_CENTER);
+        pane.setPrefWidth(5000);
+        pane.setMinWidth(400);
+        pane.setMinHeight(400);
+
+        label.setPadding(new Insets(10));
+        label.setFont(Font.font(50));
+
+        pane.getChildren().add(label);
+        pane.getChildren().add(getScrollPane());
+
+        return pane;
+    }
+
+    public static ScrollPane scrollPane = new ScrollPane();
+    public static GridPane gridPane = new GridPane();
+
+    static private ScrollPane getScrollPane() {
+        gridPane.setAlignment(Pos.TOP_CENTER);
+
+        scrollPane.setContent(gridPane);
+        scrollPane.fitToWidthProperty().set(true);
+        gridPane.setBackground(new Background(new BackgroundFill(new Color(1, 1, 1, 1), new CornerRadii(10), Insets.EMPTY)));
+
+        try {
+            List<Level> levelList = DataManager.getLevels();
+            for (int i = 0; i < levelList.size(); i++) {
+                Level level = levelList.get(i);
+                gridPane.add(getLevelPane(level, i), 1, i);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+
+        return scrollPane;
+    }
+
+    static private int selected = -1;
+    static private Pane getLevelPane(Level level, int index) {
+        GridPane pane = new GridPane(2, 2);
+        Label title = new Label(level.getName());
+        title.setFont(Font.font(30));
+        Label creator = new Label(level.getCreator());
+        creator.setFont(Font.font(20));
+        pane.add(title, 1, 1);
+        pane.add(creator, 1, 2);
+
+        pane.setOnMouseClicked(mouseEvent -> {
+            if (selected == index) {
+                log.info("Launched level {}", level.getName());
+            }
+            selected = index;
+            for (Node node : pane.getParent().getChildrenUnmodifiable()) {
+                ((Pane) node).setBackground(new Background(new BackgroundFill(new Color(1, 1, 1, 1), new CornerRadii(10), Insets.EMPTY)));
+            }
+            pane.setBackground(new Background(new BackgroundFill(new Color(0.8, 0.8, 0.8, 0.5), new CornerRadii(10), Insets.EMPTY)));
+            System.out.println(level.getName());
+        });
+
+        return pane;
+    }
+}

+ 61 - 0
ui/src/main/java/xyz/tbvns/ui/Main.java

@@ -0,0 +1,61 @@
+package xyz.tbvns.ui;
+
+import javafx.application.Application;
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.Pane;
+import javafx.stage.Stage;
+import lombok.Lombok;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FileUtils;
+import oshi.SystemInfo;
+import xyz.tbvns.core.DataManager;
+import xyz.tbvns.core.Decoder;
+import xyz.tbvns.core.FilesLocations;
+import xyz.tbvns.core.Objects.Level;
+import xyz.tbvns.core.XmlUtils;
+
+import java.io.File;
+import java.util.Arrays;
+
+@Slf4j
+public class Main extends Application {
+    public static void main(String[] args) {
+        launch(args);
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        log.info("Detected os: {}", new SystemInfo().getOperatingSystem().getFamily());
+        log.info("GD Path for this os: {}", FilesLocations.getGeometryDashFolder());
+
+        Pane levelSelect = LevelSelectScreen.pane();
+        Scene scene = new Scene(levelSelect);
+
+        scene.widthProperty().addListener((observableValue, number, t1) -> {
+            LevelSelectScreen.label.setPrefWidth(t1.floatValue() - 20);
+            LevelSelectScreen.scrollPane.setPrefWidth(t1.floatValue() - 20);
+            LevelSelectScreen.gridPane.setPrefWidth(t1.floatValue() - 15);
+            for (Node child : LevelSelectScreen.gridPane.getChildren()) {
+                ((GridPane) child).setPrefWidth(t1.floatValue() - 15);
+            }
+        });
+
+        scene.heightProperty().addListener((observableValue, number, t1) -> {
+            LevelSelectScreen.scrollPane.setPrefHeight(
+                    t1.floatValue() + 5 -
+                    LevelSelectScreen.label.getHeight() -
+                    LevelSelectScreen.label.getInsets().getTop() -
+                    LevelSelectScreen.label.getInsets().getBottom()
+            );
+        });
+
+        stage.setScene(scene);
+        stage.setTitle("PowerGD - Set of useful tools for creators");
+        stage.setMinWidth(levelSelect.getMinWidth());
+        stage.setMinHeight(levelSelect.getMinHeight());
+
+        stage.show();
+    }
+}

+ 16 - 0
ui/src/main/resources/logback.xml

@@ -0,0 +1,16 @@
+<configuration debug="false" scan="true">
+
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <!-- encoders are  by default assigned the type
+             ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
+        <encoder>
+            <pattern>%d{HH:mm:ss} [%thread] %-5level[%class] L%L - %msg%n</pattern>
+        </encoder>
+    </appender>
+
+    <root level="INFO">
+        <appender-ref ref="STDOUT" />
+    </root>
+
+    <logger name="ch.qos.logback" level="OFF" additivity="false" />
+</configuration>