Explorar o código

innoextract extraction

Laserlicht hai 1 ano
pai
achega
b1de501716

+ 143 - 34
launcher/modManager/chroniclesextractor.cpp

@@ -11,48 +11,157 @@
 
 #include "chroniclesextractor.h"
 
-void ChroniclesExtractor::installExe(QWidget *parent, QStringList exe)
+#include "../../lib/VCMIDirs.h"
+
+#ifdef ENABLE_INNOEXTRACT
+#include "cli/extract.hpp"
+#include "setup/version.hpp"
+#endif
+
+ChroniclesExtractor::ChroniclesExtractor(QWidget *p, std::function<void(float percent)> cb) :
+	parent(p), cb(cb)
 {
-	for(QString f : exe)
+}
+
+bool ChroniclesExtractor::handleTempDir(bool create)
+{
+	if(create)
 	{
-		QFile file(f);
-		if(!file.open(QIODevice::ReadOnly))
+		tempDir = QDir(pathToQString(VCMIDirs::get().userDataPath()));
+		if(tempDir.cd("tmp"))
 		{
-			QMessageBox::critical(parent, tr("File cannot opened"), file.errorString());
-			continue;
+			tempDir.removeRecursively(); // remove if already exists (e.g. previous run)
+			tempDir.cdUp();
 		}
+		tempDir.mkdir("tmp");
+		if(!tempDir.cd("tmp"))
+			return false; // should not happen - but avoid deleting wrong folder in any case
+	}
+	else
+		tempDir.removeRecursively();
+
+	return true;
+}
+
+int ChroniclesExtractor::getChronicleNo(QFile & file)
+{
+	if(!file.open(QIODevice::ReadOnly))
+	{
+		QMessageBox::critical(parent, tr("File cannot opened"), file.errorString());
+		return 0;
+	}
 
-		QByteArray magic{"MZ"};
-		QByteArray magicFile = file.read(magic.length());
-		if(!magicFile.startsWith(magic))
+	QByteArray magic{"MZ"};
+	QByteArray magicFile = file.read(magic.length());
+	if(!magicFile.startsWith(magic))
+	{
+		QMessageBox::critical(parent, tr("Invalid file selected"), tr("You have to select an gog installer file!"));
+		return 0;
+	}
+
+	QByteArray dataBegin = file.read(1'000'000);
+	const std::map<int, QByteArray> chronicles = {
+		{1, QByteArray{reinterpret_cast<const char*>(u"Heroes Chronicles - Warlords of the Wasteland"), 90}},
+		{2, QByteArray{reinterpret_cast<const char*>(u"Heroes Chronicles - Conquest of the Underworld"), 92}},
+		{3, QByteArray{reinterpret_cast<const char*>(u"Heroes Chronicles - Masters of the Elements"), 86}},
+		{4, QByteArray{reinterpret_cast<const char*>(u"Heroes Chronicles - Clash of the Dragons"), 80}},
+		{5, QByteArray{reinterpret_cast<const char*>(u"Heroes Chronicles - The World Tree"), 68}},
+		{6, QByteArray{reinterpret_cast<const char*>(u"Heroes Chronicles - The Fiery Moon"), 68}},
+		{7, QByteArray{reinterpret_cast<const char*>(u"Heroes Chronicles - Revolt of the Beastmasters"), 92}},
+		{8, QByteArray{reinterpret_cast<const char*>(u"Heroes Chronicles - The Sword of Frost"), 76}}
+	};
+	int chronicle = 0;
+	for (const auto& kv : chronicles) {
+		if(dataBegin.contains(kv.second))
 		{
-			QMessageBox::critical(parent, tr("Invalid file selected"), tr("You have to select an gog installer file!"));
-			continue;
+			chronicle = kv.first;
+			break;
 		}
+	}
+	if(!chronicle)
+	{
+		QMessageBox::critical(parent, tr("Invalid file selected"), tr("You have to select an chronicle installer file!"));
+		return 0;
+	}
+	return chronicle;
+}
 
-		QByteArray dataBegin = file.read(10'000'000);
-		const std::map<int, QByteArray> chronicles = {
-			{1, QByteArray{reinterpret_cast<const char*>(u"Heroes Chronicles - Warlords of the Wasteland"), 90}},
-			{2, QByteArray{reinterpret_cast<const char*>(u"Heroes Chronicles - Conquest of the Underworld"), 92}},
-			{3, QByteArray{reinterpret_cast<const char*>(u"Heroes Chronicles - Masters of the Elements"), 86}},
-			{4, QByteArray{reinterpret_cast<const char*>(u"Heroes Chronicles - Clash of the Dragons"), 80}},
-			{5, QByteArray{reinterpret_cast<const char*>(u"Heroes Chronicles - The World Tree"), 68}},
-			{6, QByteArray{reinterpret_cast<const char*>(u"Heroes Chronicles - The Fiery Moon"), 68}},
-			{7, QByteArray{reinterpret_cast<const char*>(u"Heroes Chronicles - Revolt of the Beastmasters"), 92}},
-			{8, QByteArray{reinterpret_cast<const char*>(u"Heroes Chronicles - The Sword of Frost"), 76}}
-		};
-		int chronicle = 0;
-		for (const auto& kv : chronicles) {
-			if(dataBegin.contains(kv.second))
-			{
-				chronicle = kv.first;
-				break;
-			}
-		}
-		if(!chronicle)
-		{
-			QMessageBox::critical(parent, tr("Invalid file selected"), tr("You have to select an chronicle installer file!"));
-			continue;
+bool ChroniclesExtractor::extractGogInstaller(QString file)
+{
+#ifndef ENABLE_INNOEXTRACT
+		QMessageBox::critical(parent, tr("Innoextract functionality missing"), "VCMI was compiled without innoextract support, which is needed to extract chroncles!");
+		return false;
+#else
+		::extract_options o;
+		o.extract = true;
+
+		// standard settings
+		o.gog_galaxy = true;
+		o.codepage = 0U;
+		o.output_dir = tempDir.path().toStdString();
+		o.extract_temp = true;
+		o.extract_unknown = true;
+		o.filenames.set_expand(true);
+
+		o.preserve_file_times = true; // also correctly closes file -> without it: on Windows the files are not written completely
+
+		QString errorText = "";
+		try
+		{
+			process_file(file.toStdString(), o, [this](float progress) {
+				float overallProgress = ((1.0 / float(fileCount)) * float(extractionFile)) + (progress / float(fileCount));
+				if(cb)
+					cb(overallProgress);
+			});
 		}
+		catch(const std::ios_base::failure & e)
+		{
+			errorText = tr("Stream error while extracting files!\nerror reason: ");
+			errorText += e.what();
+		}
+		catch(const format_error & e)
+		{
+			errorText = e.what();
+		}
+		catch(const std::runtime_error & e)
+		{
+			errorText = e.what();
+		}
+		catch(const setup::version_error &)
+		{
+			errorText = tr("Not a supported Inno Setup installer!");
+		}
+
+		if(!errorText.isEmpty())
+		{
+			QMessageBox::critical(parent, tr("Extracting error!"), errorText);
+			return false;
+		}
+
+		return true;
+#endif
+}
+
+void ChroniclesExtractor::installChronicles(QStringList exe)
+{
+	extractionFile = -1;
+	fileCount = exe.size();
+	for(QString f : exe)
+	{
+		extractionFile++;
+		QFile file(f);
+
+		int chronicleNo = getChronicleNo(file);
+		if(!chronicleNo)
+			continue;
+
+		if(!handleTempDir(true))
+			continue;
+
+		if(!extractGogInstaller(f))
+			continue;
+
+		if(!handleTempDir(false))
+			continue;
 	}
 }

+ 13 - 1
launcher/modManager/chroniclesextractor.h

@@ -15,6 +15,18 @@ class ChroniclesExtractor : public QObject
 {
 	Q_OBJECT
 
+	QWidget *parent;
+	std::function<void(float percent)> cb;
+
+	QDir tempDir;
+	int extractionFile;
+	int fileCount;
+
+	bool handleTempDir(bool create);
+	int getChronicleNo(QFile & file);
+	bool extractGogInstaller(QString filePath);
 public:
-	static void installExe(QWidget *parent, QStringList exe);
+	void installChronicles(QStringList exe);
+
+	ChroniclesExtractor(QWidget *p, std::function<void(float percent)> cb = nullptr);
 };

+ 4 - 1
launcher/modManager/cmodlistview_moc.cpp

@@ -837,7 +837,10 @@ void CModListView::installFiles(QStringList files)
 		installMaps(maps);
 
 	if(!exe.empty())
-		ChroniclesExtractor::installExe(this, exe);
+	{
+		ChroniclesExtractor ce(this);
+		ce.installChronicles(exe, [](float progress) { });
+	}
 
 	if(!images.empty())
 		loadScreenshots();