You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
225 lines
6.7 KiB
225 lines
6.7 KiB
#include "giantswdefimporter.h"
|
|
|
|
#include <QOperatingSystemVersion>
|
|
#include <QSettings>
|
|
#include <QMessageBox>
|
|
#include <QFileDialog>
|
|
#include <QFile>
|
|
#include <QProcess>
|
|
#include <QDebug>
|
|
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
#include "zlib.h"
|
|
|
|
GiantsWdefImporter::GiantsWdefImporter()
|
|
{
|
|
|
|
}
|
|
|
|
fs::path GiantsWdefImporter::askGiantsDirectory()
|
|
{
|
|
QMessageBox::information(
|
|
nullptr,
|
|
"wdefs",
|
|
"Could not locate your Giants installation automatically.\nPlease browse to your Giants folder and select GiantsMain.exe."
|
|
);
|
|
|
|
QString filename { QFileDialog::getOpenFileName(nullptr, "wdefs", "C:\\", "GiantsMain.exe") };
|
|
|
|
if (!filename.isEmpty()) {
|
|
return fs::path(filename.toStdString()).parent_path();
|
|
}
|
|
|
|
return fs::path();
|
|
}
|
|
|
|
fs::path GiantsWdefImporter::locateGiantsFolder()
|
|
{
|
|
if (QOperatingSystemVersion::Windows == QOperatingSystemVersion::currentType()) {
|
|
std::vector<std::string> possiblePaths {
|
|
"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 (auto& path : possiblePaths) {
|
|
if (fs::exists(path + "\\GiantsMain.exe")) {
|
|
return fs::path(path);
|
|
}
|
|
}
|
|
|
|
QSettings reg {"HKEY_CURRENT_USER\\Software\\PlanetMoon\\Giants", QSettings::NativeFormat };
|
|
|
|
QString regPath { reg.value("DestDir", QString()).toString().replace("\\", "\\\\") };
|
|
std::string fullPath { regPath.toStdString() + "\\GiantsMain.exe" };
|
|
|
|
if (fs::exists(fullPath)) {
|
|
return fs::path(regPath.toStdString());
|
|
}
|
|
}
|
|
|
|
return askGiantsDirectory();
|
|
}
|
|
|
|
void GiantsWdefImporter::backupFile(fs::path const& path)
|
|
{
|
|
std::string parent = path.parent_path().string();
|
|
std::string stem = path.stem().string();
|
|
|
|
std::string dest { fs::path(path.parent_path() / path.stem()).string().append("_original").append(path.extension().string()) };
|
|
|
|
if (fs::exists(dest)) {
|
|
return;
|
|
}
|
|
|
|
// TODO implement error handling
|
|
fs::copy(path, dest);
|
|
}
|
|
|
|
void GiantsWdefImporter::revertFile(const std::filesystem::path& path)
|
|
{
|
|
std::string dest { fs::path(path.parent_path() / path.stem()).string().append("_original").append(path.extension().string()) };
|
|
|
|
if (!fs::exists(dest)) {
|
|
QMessageBox::critical(nullptr, "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.");
|
|
|
|
exit(-1);
|
|
}
|
|
|
|
// TODO implement error handling
|
|
fs::copy(dest, path, fs::copy_options::overwrite_existing);
|
|
}
|
|
|
|
void GiantsWdefImporter::giantsFinished(const std::filesystem::path& giantsMainPath, const std::filesystem::path& wdefsPath, QPushButton* btn)
|
|
{
|
|
revertFile(giantsMainPath);
|
|
revertFile(wdefsPath);
|
|
|
|
QMessageBox::information(nullptr, "wdefs", "Giants has been reverted to original state.");
|
|
|
|
btn->setText("Play");
|
|
btn->setEnabled(true);
|
|
}
|
|
|
|
void GiantsWdefImporter::setParent(QObject* parent)
|
|
{
|
|
_parent = parent;
|
|
}
|
|
|
|
int GiantsWdefImporter::crc(const QByteArray &bytes)
|
|
{
|
|
uLong prev { crc32(0L, reinterpret_cast<const unsigned char*>(bytes.data()), bytes.length()) };
|
|
return static_cast<int>(prev);
|
|
}
|
|
|
|
QByteArray GiantsWdefImporter::newExeBytes(const QByteArray& wdefsBytes, const std::filesystem::path& giantsMainPath)
|
|
{
|
|
// Look for checksum in exe file
|
|
|
|
// subject to change
|
|
QByteArray checksum { "\x78\x05\x44\xb6" }; // should be at offset 0x152bff in giantsmain.exe
|
|
|
|
QFile exeFile { QString::fromStdString(giantsMainPath.string()) };
|
|
|
|
if (!exeFile.exists()) {
|
|
QMessageBox::critical(nullptr, "wdefs", "GiantsMain.exe doesn't exist in " + QString::fromStdString(giantsMainPath.string()) + "!");
|
|
}
|
|
|
|
if (!exeFile.open(QIODevice::ReadOnly)) {
|
|
QMessageBox::critical(nullptr, "wdefs", "Unable to open GiantsMain.exe for reading.");
|
|
}
|
|
|
|
QByteArray content { exeFile.readAll() };
|
|
|
|
int index { content.indexOf(checksum) };
|
|
|
|
if (index < 0) {
|
|
QMessageBox::critical(nullptr, "wdefs", "Could not find wdefs checksum in embedded GiantsMain.exe. If you are 100% sure this is unmodded Giants 1.502.1 please report this to Amazed#0001 on Discord or create an issue on the Git project.");
|
|
exit(-1);
|
|
}
|
|
|
|
int wdefsCrc { crc(wdefsBytes) };
|
|
|
|
QByteArray wdefsCrcBytes;
|
|
wdefsCrcBytes.setNum(wdefsCrc);
|
|
|
|
content.replace(index, checksum.length(), wdefsCrcBytes);
|
|
|
|
return content;
|
|
}
|
|
|
|
GiantsWdefImporter::StartResult GiantsWdefImporter::startGiantsWithWdefsBytes(QByteArray const& wdefsBytes, fs::path const& wdefsPath, fs::path const& giantsMainPath, QPushButton* button)
|
|
{
|
|
// Try writing the wdefs file
|
|
|
|
// Switching to QFile here from filesystem for ease of use
|
|
QFile wdefsFile(QString::fromStdString(wdefsPath.string()));
|
|
wdefsFile.open(QIODevice::WriteOnly);
|
|
|
|
bool wasWriteSuccess = -1 != wdefsFile.write(wdefsBytes);
|
|
|
|
if (!wasWriteSuccess) {
|
|
return StartResult::FAILURE;
|
|
}
|
|
|
|
// Try writing the exe file
|
|
|
|
QByteArray newBytes = newExeBytes(wdefsBytes, giantsMainPath);
|
|
|
|
QFile exeFile(QString::fromStdString(giantsMainPath.string()));
|
|
exeFile.open(QIODevice::WriteOnly);
|
|
|
|
wasWriteSuccess = -1 != exeFile.write(newBytes);
|
|
|
|
if (!wasWriteSuccess) {
|
|
return StartResult::FAILURE;
|
|
}
|
|
|
|
// Try starting GiantsMain.exe
|
|
|
|
if (QOperatingSystemVersion::Windows != QOperatingSystemVersion::currentType()) {
|
|
QMessageBox::warning(nullptr, "wdefs", "For now, this program only works on Windows. Please consider contacting taibsu#1841 or Amazed#0001 in the GiantsWD Discord channel for further usage.");
|
|
|
|
return StartResult::FAILURE;
|
|
}
|
|
|
|
_giantsMainProcess = new QProcess(_parent);
|
|
|
|
QObject::connect(_giantsMainProcess, &QProcess::stateChanged, [&](QProcess::ProcessState state) {
|
|
qDebug() << "[STATE] " << state;
|
|
switch (state) {
|
|
case QProcess::ProcessState::Starting:
|
|
button->setText("Starting");
|
|
break;
|
|
case QProcess::ProcessState::Running:
|
|
button->setText("Running.");
|
|
break;
|
|
default:
|
|
button->setText("Not running.");
|
|
break;
|
|
}
|
|
});
|
|
|
|
QObject::connect(_giantsMainProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), [&]() {
|
|
qDebug() << "[FINISHED]";
|
|
giantsFinished(giantsMainPath, wdefsPath, button);
|
|
});
|
|
QObject::connect(_giantsMainProcess, &QProcess::errorOccurred, [&]() {
|
|
qDebug() << "[ERROR] " << _giantsMainProcess->errorString();
|
|
giantsFinished(giantsMainPath, wdefsPath, button);
|
|
});
|
|
|
|
_giantsMainProcess->setWorkingDirectory(QString::fromStdString(giantsMainPath.parent_path().string()));
|
|
_giantsMainProcess->start(QString::fromStdString(giantsMainPath.string()), { "-launcher" });
|
|
|
|
// temporary
|
|
_giantsMainProcess->waitForFinished();
|
|
|
|
return StartResult::SUCCESS;
|
|
}
|