GDLVL/GMD conversion support & Migrate to PySide6

This commit is contained in:
Lncvrt committed 2026-10-20 18:00:40 -07:00
1 parent 69a8af761f
commit 8b750d719c
5 files changed
+91 -30

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
.DS_Store
build/
+12
View File
@@ -0,0 +1,12 @@
# macOS
nuitka --standalone --enable-plugin=pyside6 --macos-create-app-bundle --output-dir=build GDUtils.py
# Windows
nuitka --standalone --enable-plugin=pyside6 --windows-console-mode=disable --output-dir=build GDUtils.py
# Linux
nuitka --standalone --enable-plugin=pyside6 --output-dir=build GDUtils.py
+68 -24
View File
@@ -1,17 +1,13 @@
import sys import sys
from PyQt6.QtCore import Qt from PySide6.QtCore import Qt
from PyQt6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QWidget, QLabel, QPushButton, QFileDialog, QInputDialog, QMessageBox, QCheckBox from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QWidget, QLabel, QPushButton, QFileDialog, QInputDialog, QMessageBox, QCheckBox
from PyQt6.QtGui import QFont from PySide6.QtGui import QFont
from Crypto.Cipher import AES from Crypto.Cipher import AES
import base64 import base64
import gzip import gzip
import zlib import zlib
import pyperclip import pyperclip
import hashlib import hashlib
import platform
if platform.system() == "Windows":
from winmica import EnableMica, BackdropType, is_mica_supported
MAC_SAVE_KEY = ( MAC_SAVE_KEY = (
b"\x69\x70\x75\x39\x54\x55\x76\x35\x34\x79\x76\x5d\x69\x73\x46\x4d" b"\x69\x70\x75\x39\x54\x55\x76\x35\x34\x79\x76\x5d\x69\x73\x46\x4d"
@@ -98,7 +94,6 @@ class MainWindow(QMainWindow):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.setWindowTitle("GDUtils") self.setWindowTitle("GDUtils")
self.apply_mica(self)
titleLabel = QLabel("GDUtils") titleLabel = QLabel("GDUtils")
font = QFont() font = QFont()
@@ -136,6 +131,19 @@ class MainWindow(QMainWindow):
levelDataButtons.addWidget(self.levelDataIsOfficial, alignment=Qt.AlignmentFlag.AlignHCenter) levelDataButtons.addWidget(self.levelDataIsOfficial, alignment=Qt.AlignmentFlag.AlignHCenter)
levelDataButtons.addStretch() levelDataButtons.addStretch()
gdLvlGDShareLabel = QLabel("GDLVL/GDShare")
self.convertToGdLvl = QPushButton("Convert to GDLVL")
self.convertToGdLvl.clicked.connect(self.on_convert_from_gmd_click)
self.convertToGmd = QPushButton("Convert to GMD")
self.convertToGmd.clicked.connect(self.on_convert_from_gdlvl_click)
gdLvlGDShareButtons = QHBoxLayout()
gdLvlGDShareButtons.addStretch()
gdLvlGDShareButtons.addWidget(self.convertToGdLvl, alignment=Qt.AlignmentFlag.AlignHCenter)
gdLvlGDShareButtons.addWidget(self.convertToGmd, alignment=Qt.AlignmentFlag.AlignHCenter)
gdLvlGDShareButtons.addStretch()
gjpLabel = QLabel("GJP") gjpLabel = QLabel("GJP")
self.gjpDecode = QPushButton("Decode") self.gjpDecode = QPushButton("Decode")
@@ -160,6 +168,8 @@ class MainWindow(QMainWindow):
layout.addLayout(saveFileButtons) layout.addLayout(saveFileButtons)
layout.addWidget(levelDataLabel, alignment=Qt.AlignmentFlag.AlignHCenter) layout.addWidget(levelDataLabel, alignment=Qt.AlignmentFlag.AlignHCenter)
layout.addLayout(levelDataButtons) layout.addLayout(levelDataButtons)
layout.addWidget(gdLvlGDShareLabel, alignment=Qt.AlignmentFlag.AlignHCenter)
layout.addLayout(gdLvlGDShareButtons)
layout.addWidget(gjpLabel, alignment=Qt.AlignmentFlag.AlignHCenter) layout.addWidget(gjpLabel, alignment=Qt.AlignmentFlag.AlignHCenter)
layout.addLayout(gjpButtons) layout.addLayout(gjpButtons)
layout.addWidget(gjp2Label, alignment=Qt.AlignmentFlag.AlignHCenter) layout.addWidget(gjp2Label, alignment=Qt.AlignmentFlag.AlignHCenter)
@@ -170,12 +180,6 @@ class MainWindow(QMainWindow):
container.setLayout(layout) container.setLayout(layout)
self.setCentralWidget(container) self.setCentralWidget(container)
def apply_mica(self, widget):
if platform.system() == "Windows" and is_mica_supported():
widget.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, True)
hwnd = int(widget.winId())
EnableMica(hwnd, BackdropType.MICA)
def on_save_decode_click(self): def on_save_decode_click(self):
file_path, _ = QFileDialog.getOpenFileName( file_path, _ = QFileDialog.getOpenFileName(
self, self,
@@ -237,7 +241,6 @@ class MainWindow(QMainWindow):
dialog = QInputDialog(self) dialog = QInputDialog(self)
dialog.setWindowTitle("Level decoder") dialog.setWindowTitle("Level decoder")
dialog.setLabelText("Enter the level data to decode:") dialog.setLabelText("Enter the level data to decode:")
self.apply_mica(dialog)
ok = dialog.exec() ok = dialog.exec()
data = dialog.textValue() data = dialog.textValue()
if ok and data: if ok and data:
@@ -245,14 +248,12 @@ class MainWindow(QMainWindow):
msg = QMessageBox() msg = QMessageBox()
msg.setWindowTitle("Level decoder") msg.setWindowTitle("Level decoder")
msg.setText("The decoded level data has been copied to your clipboard") msg.setText("The decoded level data has been copied to your clipboard")
self.apply_mica(msg)
msg.exec() msg.exec()
def on_level_encode_click(self): def on_level_encode_click(self):
dialog = QInputDialog(self) dialog = QInputDialog(self)
dialog.setWindowTitle("Level encoder") dialog.setWindowTitle("Level encoder")
dialog.setLabelText("Enter the level data to encode:") dialog.setLabelText("Enter the level data to encode:")
self.apply_mica(dialog)
ok = dialog.exec() ok = dialog.exec()
data = dialog.textValue() data = dialog.textValue()
if ok and data: if ok and data:
@@ -260,14 +261,62 @@ class MainWindow(QMainWindow):
msg = QMessageBox() msg = QMessageBox()
msg.setWindowTitle("Level encoder") msg.setWindowTitle("Level encoder")
msg.setText("The encoded level data has been copied to your clipboard") msg.setText("The encoded level data has been copied to your clipboard")
self.apply_mica(msg)
msg.exec() msg.exec()
def on_convert_from_gdlvl_click(self):
file_path, _ = QFileDialog.getOpenFileName(
self,
"Select a GDLVL file",
"",
"GDLVL Files (*.gdlvl)"
)
if not file_path:
return
output_file_path, _ = QFileDialog.getSaveFileName(
self,
"Select a location for the output GMD file",
file_path.removesuffix(".gdlvl") + ".gmd",
"GMD Files (*.gmd)"
)
if not output_file_path:
return
with open(file_path, "r") as f:
data = f.read()
with open(output_file_path, "w") as f:
f.write(decode_level(data, False))
def on_convert_from_gmd_click(self):
file_path, _ = QFileDialog.getOpenFileName(
self,
"Select a GMD file",
"",
"GMD Files (*.gmd)"
)
if not file_path:
return
output_file_path, _ = QFileDialog.getSaveFileName(
self,
"Select a location for the output GDLVL file",
file_path.removesuffix(".gmd") + ".gdlvl",
"GDLVL Files (*.gdlvl)"
)
if not output_file_path:
return
with open(file_path, "r") as f:
data = f.read()
with open(output_file_path, "w") as f:
f.write(encode_level(data, False))
def on_gjp_decode_click(self): def on_gjp_decode_click(self):
dialog = QInputDialog(self) dialog = QInputDialog(self)
dialog.setWindowTitle("GJP decoder") dialog.setWindowTitle("GJP decoder")
dialog.setLabelText("Enter the GJP to decode:") dialog.setLabelText("Enter the GJP to decode:")
self.apply_mica(dialog)
ok = dialog.exec() ok = dialog.exec()
data = dialog.textValue() data = dialog.textValue()
if ok and data: if ok and data:
@@ -275,14 +324,12 @@ class MainWindow(QMainWindow):
msg = QMessageBox() msg = QMessageBox()
msg.setWindowTitle("GJP decoder") msg.setWindowTitle("GJP decoder")
msg.setText("The decoded GJP has been copied to your clipboard") msg.setText("The decoded GJP has been copied to your clipboard")
self.apply_mica(msg)
msg.exec() msg.exec()
def on_gjp_encode_click(self): def on_gjp_encode_click(self):
dialog = QInputDialog(self) dialog = QInputDialog(self)
dialog.setWindowTitle("GJP encoder") dialog.setWindowTitle("GJP encoder")
dialog.setLabelText("Enter the password to encode:") dialog.setLabelText("Enter the password to encode:")
self.apply_mica(dialog)
ok = dialog.exec() ok = dialog.exec()
data = dialog.textValue() data = dialog.textValue()
if ok and data: if ok and data:
@@ -290,14 +337,12 @@ class MainWindow(QMainWindow):
msg = QMessageBox() msg = QMessageBox()
msg.setWindowTitle("GJP encoder") msg.setWindowTitle("GJP encoder")
msg.setText("The encoded GJP has been copied to your clipboard") msg.setText("The encoded GJP has been copied to your clipboard")
self.apply_mica(msg)
msg.exec() msg.exec()
def on_gjp2_encode_click(self): def on_gjp2_encode_click(self):
dialog = QInputDialog(self) dialog = QInputDialog(self)
dialog.setWindowTitle("GJP2 encoder") dialog.setWindowTitle("GJP2 encoder")
dialog.setLabelText("Enter the password to encode:") dialog.setLabelText("Enter the password to encode:")
self.apply_mica(dialog)
ok = dialog.exec() ok = dialog.exec()
data = dialog.textValue() data = dialog.textValue()
if ok and data: if ok and data:
@@ -305,7 +350,6 @@ class MainWindow(QMainWindow):
msg = QMessageBox() msg = QMessageBox()
msg.setWindowTitle("GJP2 encoder") msg.setWindowTitle("GJP2 encoder")
msg.setText("The encoded GJP2 has been copied to your clipboard") msg.setText("The encoded GJP2 has been copied to your clipboard")
self.apply_mica(msg)
msg.exec() msg.exec()
app = QApplication(sys.argv) app = QApplication(sys.argv)
+8 -4
View File
@@ -9,10 +9,14 @@ A simple python utility app for things related to Geometry Dash
- GJP encode/decode - GJP encode/decode
- GJP2 encode - GJP2 encode
## Setup ## Download
Download the latest release for your platform [here](https://git.lncvrt.xyz/Lncvrt/gdutils/releases)
## Run manually with Python
[Download the repository as a ZIP](https://git.lncvrt.xyz/Lncvrt/gdutils/archive/main.zip) and unzip it then run: [Download the repository as a ZIP](https://git.lncvrt.xyz/Lncvrt/gdutils/archive/main.zip) and unzip it then run:
`pip install -r requirements.txt` (you may need to replace pip with pip3) `pip install -r requirements.txt` then run `python GDUtils.py`
then run
`python app.py` (you may need to replace python with python3) (you may need to replace pip and python with pip3 and python3)
+1 -2
View File
@@ -1,4 +1,3 @@
PyQt6 pyside6
pycryptodome pycryptodome
pyperclip pyperclip
winmica ; sys_platform == 'win32'