This commit is contained in:
Amazed 2022-01-09 19:58:39 +01:00
commit 75d14bc5f6
6 changed files with 314 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
venv/
__pycache__/
*.pyc
.idea/

4
build.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
for ui in *.ui; do
pyside6-uic $ui > ${ui%.*}.py
done

174
main.py Normal file
View File

@ -0,0 +1,174 @@
import os
import struct
import subprocess
import sys
import traceback
from pathlib import Path
import shutil
import zlib
import requests
import threading
from PySide6.QtCore import QProcess
from PySide6.QtWidgets import QApplication, QMainWindow, QFileDialog, QMessageBox, QTreeWidgetItem, QLayout, QLabel, \
QLineEdit, QPushButton
from mainwindow import Ui_MainWindow
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.giants_path = locate_giants_folder()
self.wdefs_path = self.giants_path / "Bin" / "wdefs.bin"
self.giantsmain_path = self.giants_path / "GiantsMain.exe"
if not self.giants_path:
self.close()
self.ui.lineEdit.textEdited.connect(self.on_textedit_changed)
self.ui.pushButton.clicked.connect(self.on_button_clicked)
def on_textedit_changed(self):
if self.ui.lineEdit.text():
self.ui.pushButton.setEnabled(True)
else:
self.ui.pushButton.setEnabled(False)
def on_button_clicked(self):
self.ui.pushButton.setEnabled(False)
try:
backup_file(self.wdefs_path)
backup_file(self.giantsmain_path)
except Exception:
traceback.print_exc()
QMessageBox.critical(None, "Wdefs", "Error while creating backups.")
return
text = self.ui.lineEdit.text()
if not text:
QMessageBox.critical(None, "Wdefs", "URL is empty")
return
try:
req = requests.get(text)
req.raise_for_status()
start_giants_with_wdefs_bytes(req.content, self.wdefs_path, self.giantsmain_path, self.ui.pushButton)
except requests.HTTPError:
traceback.print_exc()
QMessageBox.critical(None, "Wdefs", "Could not download wdefs at URL.")
except Exception:
try:
revert_files(self.giantsmain_path)
revert_files(self.wdefs_path)
traceback.print_exc()
QMessageBox.warning(None, "Wdefs", "There was a problem while applying mod. Your game has been restored to original state.")
return
except Exception:
traceback.print_exc()
QMessageBox.critical(None, "Wdefs", "There was a critical error while applying the mod. Your game couldn't be restored to original state. Please message the mods on Discord.")
return
def start_giants_with_wdefs_bytes(wdefs_bytes: bytes, wdefs_path: Path, giantsmain_path: Path, btn: QPushButton):
with open(wdefs_path, "wb") as fp:
fp.write(wdefs_bytes)
new_bytes = new_exe_bytes(wdefs_bytes, giantsmain_path)
with open(giantsmain_path, "wb") as fp:
fp.write(new_bytes)
def giants_finished():
revert_files(giantsmain_path)
revert_files(wdefs_path)
QMessageBox.information(None, "Wdefs", "Giants has been reverted to original state.")
btn.setText("Play")
btn.setEnabled(True)
process = QProcess(app)
process.finished.connect(giants_finished)
btn.setText("Game started")
if sys.platform == "win32":
process.start(str(giantsmain_path), ["-launcher"])
else:
process.setWorkingDirectory(str(giantsmain_path.parent))
process.start("/home/tasty/.local/share/lutris/runners/wine/lutris-6.0-x86_64/bin/wine", ["/home/tasty/Jeux/Giants Citizen Kabuto1.498/GiantsMain.exe", "-window", "-launcher"])
def revert_files(file_path: Path) -> None:
# replace current with _original
dst = str(file_path.parent / file_path.stem) + "_original" + file_path.suffix
shutil.copy(dst, file_path)
def backup_file(file_path: Path) -> None:
# copy and append _original to filename
# if file already exists, skip
dst = str(file_path.parent / file_path.stem) + "_original" + file_path.suffix
print(dst)
if os.path.exists(dst):
return
shutil.copy(file_path, dst)
def locate_giants_folder() -> Path:
is_windows = sys.platform == "win32"
if is_windows:
possible_paths = [
"C:\\Program Files (x86)\\GOG Galaxy\\Games\\Giants - Citizen Kabuto",
"C:\\Program Files\\GOG Galaxy\\Games\\Giants - Citizen Kabuto",
"C:\\Program Files (x86)\\GOG.com\\Games\\Giants - Citizen Kabuto",
"C:\\Program Files\\GOG.com\\Games\\Giants - Citizen Kabuto",
"C:\\Program Files (x86)\\Steam\\steamapps\\common\\Giants Citizen Kabuto",
"C:\\Program Files\\Steam\\steamapps\\common\\Giants Citizen Kabuto",
]
for path in possible_paths:
if os.path.exists(path+"\\GiantsMain.exe"):
return Path(path)
import winreg
try:
path = winreg.QueryValue(winreg.HKEY_CURRENT_USER, "Software\\PlanetMoon\\Giants\\DestDir")
if os.path.exists(path + "\\GiantsMain.exe"):
return Path(path)
except:
pass
return ask_giants_directory()
else:
return ask_giants_directory()
def ask_giants_directory() -> Path:
QMessageBox.information(None, "Wdefs",
"Could not locate your Giants installation automatically.\nPlease browse to your Giants folder and select GiantsMain.exe")
giantsmain_path, _filename = QFileDialog.getOpenFileName(None, caption="Wdefs", filter="GiantsMain.exe")
return Path(giantsmain_path).parent
def crc(inp_bytes: bytes) -> int:
prev = zlib.crc32(inp_bytes, 0)
return prev & 0xffffffff
def new_exe_bytes(wdefs_bytes: bytes, giantsmain_path: Path) -> bytes:
CHECKSUM_1_502_0_1 = b"\x78\x05\x44\xb6" # should be at offset 0x152bff in giantsmain.exe
with open(giantsmain_path, "rb") as fp:
content = fp.read()
index = content.find(CHECKSUM_1_502_0_1)
if index < 0:
raise Exception("Could not find wdefs checksum in embedded GiantsMain.exe. Report this to Amazed#0001")
else:
content = bytearray(content)
wdefs_crc = struct.pack("<L", crc(wdefs_bytes))
content[index:index+4] = wdefs_crc
return content
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setQuitOnLastWindowClosed(True)
window = MainWindow()
window.show()
sys.exit(app.exec())

66
mainwindow.py Normal file
View File

@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'mainwindow.ui'
##
## Created by: Qt User Interface Compiler version 6.2.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QGridLayout, QLabel, QLineEdit,
QMainWindow, QMenuBar, QPushButton, QSizePolicy,
QWidget)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
if not MainWindow.objectName():
MainWindow.setObjectName(u"MainWindow")
MainWindow.resize(548, 105)
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget")
self.gridLayoutWidget = QWidget(self.centralwidget)
self.gridLayoutWidget.setObjectName(u"gridLayoutWidget")
self.gridLayoutWidget.setGeometry(QRect(10, 10, 531, 63))
self.gridLayout = QGridLayout(self.gridLayoutWidget)
self.gridLayout.setObjectName(u"gridLayout")
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.lineEdit = QLineEdit(self.gridLayoutWidget)
self.lineEdit.setObjectName(u"lineEdit")
self.gridLayout.addWidget(self.lineEdit, 0, 1, 1, 1)
self.pushButton = QPushButton(self.gridLayoutWidget)
self.pushButton.setObjectName(u"pushButton")
self.gridLayout.addWidget(self.pushButton, 0, 2, 1, 1)
self.label = QLabel(self.gridLayoutWidget)
self.label.setObjectName(u"label")
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(MainWindow)
self.menubar.setObjectName(u"menubar")
self.menubar.setGeometry(QRect(0, 0, 548, 21))
MainWindow.setMenuBar(self.menubar)
self.retranslateUi(MainWindow)
QMetaObject.connectSlotsByName(MainWindow)
# setupUi
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"Giants wdefs importer", None))
self.pushButton.setText(QCoreApplication.translate("MainWindow", u"Play", None))
self.label.setText(QCoreApplication.translate("MainWindow", u"URL to wdefs file", None))
# retranslateUi

63
mainwindow.ui Normal file
View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>548</width>
<height>105</height>
</rect>
</property>
<property name="windowTitle">
<string>Giants wdefs importer</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="gridLayoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>531</width>
<height>63</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit"/>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="pushButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Play</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>URL to wdefs file</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>548</width>
<height>21</height>
</rect>
</property>
</widget>
</widget>
<resources/>
<connections/>
</ui>

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
PySide6
pyinstaller
requests