Bläddra i källkod

Slightly rework Chronicles installer to make it work on Android

Ivan Savenko 10 månader sedan
förälder
incheckning
80f5aa8336

+ 3 - 0
launcher/firstLaunch/firstlaunch_moc.cpp

@@ -371,6 +371,9 @@ void FirstLaunchView::extractGogData()
 		QFile(fileExe).copy(tmpFileExe);
 		QFile(fileBin).copy(tmpFileBin);
 
+		logGlobal->info("Installing exe '%s' ('%s')", tmpFileExe.toStdString(), fileExe.toStdString());
+		logGlobal->info("Installing bin '%s' ('%s')", tmpFileBin.toStdString(), fileBin.toStdString());
+
 		QString errorText{};
 
 		auto isGogGalaxyExe = [](QString fileToTest) {

+ 9 - 6
launcher/mainwindow_moc.cpp

@@ -258,12 +258,15 @@ void MainWindow::manualInstallFile(QString filePath)
 
 	QString fileName = QFileInfo{filePath}.fileName();
 	if(filePath.endsWith(".zip", Qt::CaseInsensitive))
-		getModView()->downloadFile(fileName.toLower()
-														// mod name currently comes from zip file -> remove suffixes from github zip download
-														.replace(QRegularExpression("-[0-9a-f]{40}"), "")
-														.replace(QRegularExpression("-vcmi-.+\\.zip"), ".zip")
-														.replace("-main.zip", ".zip")
-													, QUrl::fromLocalFile(filePath), "mods");
+	{
+		QString filenameClean = fileName.toLower()
+			// mod name currently comes from zip file -> remove suffixes from github zip download
+			.replace(QRegularExpression("-[0-9a-f]{40}"), "")
+			.replace(QRegularExpression("-vcmi-.+\\.zip"), ".zip")
+			.replace("-main.zip", ".zip");
+
+		getModView()->downloadFile(filenameClean, QUrl::fromLocalFile(filePath), "mods");
+	}
 	else if(filePath.endsWith(".json", Qt::CaseInsensitive))
 	{
 		QDir configDir(QString::fromStdString(VCMIDirs::get().userConfigPath().string()));

+ 37 - 36
launcher/modManager/chroniclesextractor.cpp

@@ -41,37 +41,23 @@ void ChroniclesExtractor::removeTempDir()
 	tempDir.removeRecursively();
 }
 
-int ChroniclesExtractor::getChronicleNo(QFile & file)
+int ChroniclesExtractor::getChronicleNo()
 {
-	if(!file.open(QIODevice::ReadOnly))
+	for (size_t i = 1; i < chronicles.size(); ++i)
 	{
-		QMessageBox::critical(parent, tr("The file cannot be opened"), file.errorString());
-		return 0;
-	}
+		QString chronicleName = chronicles.at(i);
 
-	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 a gog installer file!"));
-		return 0;
-	}
+		QStringList appDirCandidates = tempDir.entryList({"app"}, QDir::Filter::Dirs);
+		QDir appDir = tempDir.filePath(appDirCandidates.front());
 
-	QByteArray dataBegin = file.read(1'000'000);
-	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 a Heroes Chronicles installer file!"));
-		return 0;
+		QStringList chroniclesDirCandidates = appDir.entryList({chronicleName}, QDir::Filter::Dirs);
+
+		if (!chroniclesDirCandidates.empty())
+			return i;
 	}
-	return chronicle;
+
+	QMessageBox::critical(parent, tr("Invalid file selected"), tr("You have to select a Heroes Chronicles installer file!"));
+	return 0;
 }
 
 bool ChroniclesExtractor::extractGogInstaller(QString file)
@@ -147,14 +133,13 @@ void ChroniclesExtractor::createChronicleMod(int no)
 	dir.removeRecursively();
 	dir.mkpath(".");
 
-	QByteArray tmpChronicles = chronicles.at(no);
-	tmpChronicles.replace('\0', "");
+	QString tmpChronicles = chronicles.at(no);
 
 	QJsonObject mod
 	{
 		{ "modType", "Expansion" },
-		{ "name", QString::number(no) + " - " + QString(tmpChronicles) },
-		{ "description", tr("Heroes Chronicles") + " - " + QString::number(no) + " - " + QString(tmpChronicles) },
+		{ "name", QString::number(no) + " - " + tmpChronicles },
+		{ "description", tr("Heroes Chronicles") + " - " + QString::number(no) + " - " + tmpChronicles },
 		{ "author", "3DO" },
 		{ "version", "1.0" },
 		{ "contact", "vcmi.eu" },
@@ -171,8 +156,7 @@ void ChroniclesExtractor::createChronicleMod(int no)
 
 void ChroniclesExtractor::extractFiles(int no) const
 {
-	QByteArray tmpChronicles = chronicles.at(no);
-	tmpChronicles.replace('\0', "");
+	QString tmpChronicles = chronicles.at(no);
 
 	std::string chroniclesDir = "chronicles_" + std::to_string(no);
 	QDir tmpDir = tempDir.filePath(tempDir.entryList({"app"}, QDir::Filter::Dirs).front());
@@ -228,29 +212,46 @@ void ChroniclesExtractor::extractFiles(int no) const
 
 void ChroniclesExtractor::installChronicles(QStringList exe)
 {
+	logGlobal->info("Installing Chronicles");
+
 	extractionFile = -1;
 	fileCount = exe.size();
 	for(QString f : exe)
 	{
 		extractionFile++;
 
+		logGlobal->info("Creating temporary directory");
 		if(!createTempDir())
 			continue;
 		
+		logGlobal->info("Copying offline installer");
+		// FIXME: this is required at the moment for Android (and possibly iOS)
+		// Incoming file names are in content URI form, e.g. content://media/internal/chronicles.exe
+		// Qt can handle those like it does regular files
+		// however, innoextract fails to open such files
+		// so make a copy in directory to which vcmi always has full access and operate on it
 		QString filepath = tempDir.filePath("chr.exe");
 		QFile(f).copy(filepath);
 		QFile file(filepath);
 
-		int chronicleNo = getChronicleNo(file);
-		if(!chronicleNo)
+		logGlobal->info("Extracting offline installer");
+		if(!extractGogInstaller(filepath))
 			continue;
 
-		if(!extractGogInstaller(filepath))
+		logGlobal->info("Detecting Chronicle");
+		int chronicleNo = getChronicleNo();
+		if(!chronicleNo)
 			continue;
-		
+
+		logGlobal->info("Creating base Chronicle mod");
 		createBaseMod();
+
+		logGlobal->info("Creating Chronicle mod");
 		createChronicleMod(chronicleNo);
 
+		logGlobal->info("Removing temporary directory");
 		removeTempDir();
 	}
+
+	logGlobal->info("Chronicles installed");
 }

+ 11 - 10
launcher/modManager/chroniclesextractor.h

@@ -24,21 +24,22 @@ class ChroniclesExtractor : public QObject
 
 	bool createTempDir();
 	void removeTempDir();
-	int getChronicleNo(QFile & file);
+	int getChronicleNo();
 	bool extractGogInstaller(QString filePath);
 	void createBaseMod() const;
 	void createChronicleMod(int no);
 	void extractFiles(int no) const;
 
-	const std::map<int, QByteArray> chronicles = {
-		{1, QByteArray{reinterpret_cast<const char*>(u"Warlords of the Wasteland"), 50}},
-		{2, QByteArray{reinterpret_cast<const char*>(u"Conquest of the Underworld"), 52}},
-		{3, QByteArray{reinterpret_cast<const char*>(u"Masters of the Elements"), 46}},
-		{4, QByteArray{reinterpret_cast<const char*>(u"Clash of the Dragons"), 40}},
-		{5, QByteArray{reinterpret_cast<const char*>(u"The World Tree"), 28}},
-		{6, QByteArray{reinterpret_cast<const char*>(u"The Fiery Moon"), 28}},
-		{7, QByteArray{reinterpret_cast<const char*>(u"Revolt of the Beastmasters"), 52}},
-		{8, QByteArray{reinterpret_cast<const char*>(u"The Sword of Frost"), 36}}
+	const QStringList chronicles = {
+		"", // fake	0th "chronicle", to create 1-based list
+		"Warlords of the Wasteland",
+		"Conquest of the Underworld",
+		"Masters of the Elements",
+		"Clash of the Dragons",
+		"The World Tree",
+		"The Fiery Moon",
+		"Revolt of the Beastmasters",
+		"The Sword of Frost",
 	};
 public:
 	void installChronicles(QStringList exe);

+ 2 - 0
launcher/modManager/cmodlistview_moc.cpp

@@ -817,6 +817,8 @@ void CModListView::installFiles(QStringList files)
 		{
 			ChroniclesExtractor ce(this, [&prog](float progress) { prog = progress; });
 			ce.installChronicles(exe);
+			modStateModel->reloadLocalState();
+			modModel->reloadRepositories();
 			enableModByName("chronicles");
 			return true;
 		});

+ 3 - 0
launcher/startGame/StartGameTab.cpp

@@ -219,7 +219,10 @@ void StartGameTab::on_buttonImportFiles_clicked()
 		QStringList files = QFileDialog::getOpenFileNames(this, tr("Select files (configs, mods, maps, campaigns, gog files) to install..."), QDir::homePath(), filter);
 
 		for(const auto & file : files)
+		{
+			logGlobal->info("Importing file %s", file.toStdString());
 			getMainWindow()->manualInstallFile(file);
+		}
 	};
 
 	// iOS can't display modal dialogs when called directly on button press