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.
 
 
wdefs-importer/giantswdefimporter.cpp

224 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;
}