From 75d14bc5f68e9c3ac807e90849f19e0982d87960 Mon Sep 17 00:00:00 2001 From: Hipstercat Date: Sun, 9 Jan 2022 19:58:39 +0100 Subject: [PATCH] init --- .gitignore | 4 ++ build.sh | 4 ++ main.py | 174 +++++++++++++++++++++++++++++++++++++++++++++++ mainwindow.py | 66 ++++++++++++++++++ mainwindow.ui | 63 +++++++++++++++++ requirements.txt | 3 + 6 files changed, 314 insertions(+) create mode 100644 .gitignore create mode 100755 build.sh create mode 100644 main.py create mode 100644 mainwindow.py create mode 100644 mainwindow.ui create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..74d8cee --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +venv/ +__pycache__/ +*.pyc +.idea/ \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..647e6b4 --- /dev/null +++ b/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash +for ui in *.ui; do + pyside6-uic $ui > ${ui%.*}.py +done \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..e41c6d4 --- /dev/null +++ b/main.py @@ -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(" + + MainWindow + + + + 0 + 0 + 548 + 105 + + + + Giants wdefs importer + + + + + + 10 + 10 + 531 + 63 + + + + + + + + + + false + + + Play + + + + + + + URL to wdefs file + + + + + + + + + + 0 + 0 + 548 + 21 + + + + + + + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..bbe75c7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +PySide6 +pyinstaller +requests \ No newline at end of file