#include "giantswdefimporter.h" #include #include #include #include #include #include #include #include #include #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 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(bytes.data()), bytes.length()) }; return static_cast(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::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; }