| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 | 
							- /*
 
-  * chroniclesextractor.cpp, part of VCMI engine
 
-  *
 
-  * Authors: listed in file AUTHORS in main folder
 
-  *
 
-  * License: GNU General Public License v2.0 or later
 
-  * Full text of license available in license.txt file, in main folder
 
-  *
 
-  */
 
- #include "StdInc.h"
 
- #include "chroniclesextractor.h"
 
- #include "../../lib/VCMIDirs.h"
 
- #include "../../lib/filesystem/CArchiveLoader.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)
 
- {
 
- }
 
- bool ChroniclesExtractor::handleTempDir(bool create)
 
- {
 
- 	if(create)
 
- 	{
 
- 		tempDir = QDir(pathToQString(VCMIDirs::get().userDataPath()));
 
- 		if(tempDir.cd("tmp"))
 
- 		{
 
- 			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))
 
- 	{
 
- 		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);
 
- 	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!"));
 
- 		return 0;
 
- 	}
 
- 	return chronicle;
 
- }
 
- 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::createBaseMod()
 
- {
 
- 	QDir dir(pathToQString(VCMIDirs::get().userDataPath() / "Mods"));
 
- 	dir.mkdir("chronicles");
 
- 	dir.cd("chronicles");
 
- 	dir.mkdir("Mods");
 
- 	QJsonObject mod
 
- 	{
 
- 		{ "modType", "Expansion" },
 
- 		{ "name", "Heroes Chronicles" },
 
- 		{ "description", "" },
 
- 		{ "author", "3DO" },
 
- 		{ "version", "1.0" },
 
- 	};
 
- 	QFile jsonFile(dir.filePath("mod.json"));
 
-     jsonFile.open(QFile::WriteOnly);
 
-     jsonFile.write(QJsonDocument(mod).toJson());
 
- }
 
- void ChroniclesExtractor::createChronicleMod(int no)
 
- {
 
- 	QDir dir(pathToQString(VCMIDirs::get().userDataPath() / "Mods" / "chronicles" / "Mods" / ("chronicles_" + std::to_string(no))));
 
- 	dir.removeRecursively();
 
- 	dir.mkpath(".");
 
- 	QJsonObject mod
 
- 	{
 
- 		{ "modType", "Expansion" },
 
- 		{ "name", "Heroes Chronicles - " + QString::number(no) },
 
- 		{ "description", "" },
 
- 		{ "author", "3DO" },
 
- 		{ "version", "1.0" },
 
- 	};
 
- 	
 
- 	QFile jsonFile(dir.filePath("mod.json"));
 
-     jsonFile.open(QFile::WriteOnly);
 
-     jsonFile.write(QJsonDocument(mod).toJson());
 
- 	dir.cd("content");
 
- 	
 
- 	extractFiles(no);
 
- }
 
- void ChroniclesExtractor::extractFiles(int no)
 
- {
 
- 	QByteArray tmpChronicles = chronicles.at(no);
 
- 	tmpChronicles.replace('\0', "");
 
- 	QDir tmpDir = tempDir.filePath(tempDir.entryList({"app"}, QDir::Filter::Dirs).front());
 
- 	tmpDir.setPath(tmpDir.filePath(tmpDir.entryList({QString(tmpChronicles)}, QDir::Filter::Dirs).front()));
 
- 	tmpDir.setPath(tmpDir.filePath(tmpDir.entryList({"data"}, QDir::Filter::Dirs).front()));
 
- 	auto basePath = VCMIDirs::get().userDataPath() / "Mods" / "chronicles" / "Mods" / ("chronicles_" + std::to_string(no)) / "content";
 
- 	QDir outDirData(pathToQString(basePath / "Data"));
 
- 	QDir outDirSprites(pathToQString(basePath / "Sprites"));
 
- 	QDir outDirVideo(pathToQString(basePath / "Video"));
 
- 	QDir outDirSounds(pathToQString(basePath / "Sounds"));
 
- 	QDir outDirMaps(pathToQString(basePath / "Maps"));
 
- 	auto extract = [](QDir scrDir, QDir dest, QString file, std::vector<std::string> files = {}){
 
- 		CArchiveLoader archive("", scrDir.filePath(scrDir.entryList({file}).front()).toStdString(), false);
 
- 		for(auto & entry : archive.getEntries())
 
- 			if(files.empty())
 
- 				archive.extractToFolder(dest.absolutePath().toStdString(), "", entry.second, true);
 
- 			else
 
- 			{
 
- 				for(auto & item : files)
 
- 					if(!boost::algorithm::to_lower_copy(entry.second.name).find(boost::algorithm::to_lower_copy(item)))
 
- 						archive.extractToFolder(dest.absolutePath().toStdString(), "", entry.second, true);
 
- 			}
 
- 	};
 
- 	auto rename = [no](QDir dest){
 
- 		dest.refresh();
 
- 		for(auto & entry : dest.entryList())
 
- 			if(!entry.startsWith("Hc" + QString::number(no) + "_"))
 
- 				dest.rename(entry, "Hc" + QString::number(no) + "_" + entry);
 
- 	};
 
- 	extract(tmpDir, outDirData, "xBitmap.lod");
 
- 	extract(tmpDir, outDirData, "xlBitmap.lod");
 
- 	extract(tmpDir, outDirSprites, "xSprite.lod");
 
- 	extract(tmpDir, outDirSprites, "xlSprite.lod");
 
- 	extract(tmpDir, outDirVideo, "xVideo.vid");
 
- 	extract(tmpDir, outDirSounds, "xSound.snd");
 
- 	tmpDir.cdUp();
 
- 	if(tmpDir.entryList({"maps"}, QDir::Filter::Dirs).size())
 
- 	{
 
- 		QDir tmpDirMaps = tmpDir.filePath(tmpDir.entryList({"maps"}, QDir::Filter::Dirs).front());
 
- 		for(auto & entry : tmpDirMaps.entryList())
 
- 			QFile(tmpDirMaps.filePath(entry)).copy(outDirData.filePath(entry));
 
- 	}
 
- 	tmpDir.cdUp();
 
- 	QDir tmpDirData = tmpDir.filePath(tmpDir.entryList({"data"}, QDir::Filter::Dirs).front());
 
- 	extract(tmpDirData, outDirData, "bitmap.lod", std::vector<std::string>{"HPL003sh", "HPL102br", "HPL139", "HPS006kn", "HPS137", "HPS141", "HPL004sh", "hpl112bs", "HPL140", "hps007sh", "HPS138", "HPS142", "HPL006kn", "HPL137", "HPS003sh", "HPS102br", "HPS139", "HPS143", "hpl007sh", "HPL138", "HPS004sh", "hps112bs", "HPS140"});
 
- 	extract(tmpDirData, outDirData, "lbitmap.lod", std::vector<std::string>{"INTRORIM"});
 
- 	rename(outDirData);
 
- 	rename(outDirSprites);
 
- 	rename(outDirVideo);
 
- 	rename(outDirSounds);
 
- 	if(!outDirMaps.exists())
 
- 		outDirMaps.mkpath(".");
 
- 	QString campaignFileName = "Hc" + QString::number(no) + "_Main.h3c";
 
- 	QFile(outDirData.filePath(outDirData.entryList({campaignFileName}).front())).copy(outDirMaps.filePath(campaignFileName));
 
- }
 
- 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;
 
- 		
 
- 		createBaseMod();
 
- 		createChronicleMod(chronicleNo);
 
- 		handleTempDir(false);
 
- 	}
 
- }
 
 
  |