mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-22 22:13:35 +02:00
Merge pull request #5101 from IvanSavenko/android_chronicle
Attempt to fix Chronicles installation on Android
This commit is contained in:
commit
604aa9a6b9
@ -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) {
|
||||
|
@ -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()));
|
||||
|
@ -41,37 +41,25 @@ void ChroniclesExtractor::removeTempDir()
|
||||
tempDir.removeRecursively();
|
||||
}
|
||||
|
||||
int ChroniclesExtractor::getChronicleNo(QFile & file)
|
||||
int ChroniclesExtractor::getChronicleNo()
|
||||
{
|
||||
if(!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
QMessageBox::critical(parent, tr("The file cannot be opened"), file.errorString());
|
||||
return 0;
|
||||
}
|
||||
QStringList appDirCandidates = tempDir.entryList({"app"}, QDir::Filter::Dirs);
|
||||
|
||||
QByteArray magic{"MZ"};
|
||||
QByteArray magicFile = file.read(magic.length());
|
||||
if(!magicFile.startsWith(magic))
|
||||
if (!appDirCandidates.empty())
|
||||
{
|
||||
QMessageBox::critical(parent, tr("Invalid file selected"), tr("You have to select a gog installer file!"));
|
||||
return 0;
|
||||
}
|
||||
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))
|
||||
for (size_t i = 1; i < chronicles.size(); ++i)
|
||||
{
|
||||
chronicle = kv.first;
|
||||
break;
|
||||
QString chronicleName = chronicles.at(i);
|
||||
QStringList chroniclesDirCandidates = appDir.entryList({chronicleName}, QDir::Filter::Dirs);
|
||||
|
||||
if (!chroniclesDirCandidates.empty())
|
||||
return i;
|
||||
}
|
||||
}
|
||||
if(!chronicle)
|
||||
{
|
||||
QMessageBox::critical(parent, tr("Invalid file selected"), tr("You have to select a Heroes Chronicles installer file!"));
|
||||
return 0;
|
||||
}
|
||||
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 +135,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("%1 - %2").arg(no).arg(tmpChronicles) },
|
||||
{ "description", tr("Heroes Chronicles %1 - %2").arg(no).arg(tmpChronicles) },
|
||||
{ "author", "3DO" },
|
||||
{ "version", "1.0" },
|
||||
{ "contact", "vcmi.eu" },
|
||||
@ -171,8 +158,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 +214,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)
|
||||
continue;
|
||||
|
||||
logGlobal->info("Extracting offline installer");
|
||||
if(!extractGogInstaller(filepath))
|
||||
continue;
|
||||
|
||||
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");
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
});
|
||||
|
@ -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
|
||||
|
@ -1086,6 +1086,11 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation>英雄无敌历代记</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||
<source>Heroes Chronicles %1 - %2</source>
|
||||
<translation type="unfinished">英雄无敌历代记 %1 - %2</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>File size</name>
|
||||
|
@ -832,6 +832,11 @@ Režim celé obrazovky - hra pokryje celou vaši obrazovku a použije vybrané r
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation>Heroes Chronicles</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||
<source>Heroes Chronicles %1 - %2</source>
|
||||
<translation type="unfinished">Heroes Chronicles %1 - %2</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>File size</name>
|
||||
|
@ -1055,6 +1055,11 @@ Mode exclusif plein écran - le jeu couvrira l"intégralité de votre écra
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||
<source>Heroes Chronicles %1 - %2</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>File size</name>
|
||||
|
@ -1080,6 +1080,11 @@ Exklusiver Vollbildmodus - das Spiel bedeckt den gesamten Bildschirm und verwend
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation>Heroes Chronicles</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||
<source>Heroes Chronicles %1 - %2</source>
|
||||
<translation type="unfinished">Heroes Chronicles %1 - %2</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>File size</name>
|
||||
|
@ -1080,6 +1080,11 @@ Pełny ekran klasyczny - gra przysłoni cały ekran uruchamiając się w wybrane
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation>Heroes Kroniki</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||
<source>Heroes Chronicles %1 - %2</source>
|
||||
<translation type="unfinished">Heroes Kroniki %1 - %2</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>File size</name>
|
||||
|
@ -898,6 +898,11 @@ Modo de tela cheia exclusivo - o jogo cobrirá toda a sua tela e usará a resolu
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation>Heroes Chronicles</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||
<source>Heroes Chronicles %1 - %2</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>File size</name>
|
||||
|
@ -918,6 +918,11 @@ Fullscreen Exclusive Mode - the game will cover the entirety of your screen and
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||
<source>Heroes Chronicles %1 - %2</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>File size</name>
|
||||
|
@ -949,6 +949,11 @@ Pantalla completa - el juego cubrirá la totalidad de la pantalla y utilizará l
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||
<source>Heroes Chronicles %1 - %2</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>File size</name>
|
||||
|
@ -1074,6 +1074,11 @@ Exklusivt helskärmsläge - spelet kommer att täcka hela skärmen och använda
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation>Heroes Chronicles</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||
<source>Heroes Chronicles %1 - %2</source>
|
||||
<translation type="unfinished">Heroes Chronicles %1 - %2</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>File size</name>
|
||||
|
@ -1056,6 +1056,11 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation>Хроніки Героїв</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||
<source>Heroes Chronicles %1 - %2</source>
|
||||
<translation type="unfinished">Хроніки Героїв %1 - %2</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>File size</name>
|
||||
|
@ -934,6 +934,11 @@ Toàn màn hình riêng biệt - Trò chơi chạy toàn màn hình và dùng đ
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||
<source>Heroes Chronicles %1 - %2</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>File size</name>
|
||||
|
Loading…
Reference in New Issue
Block a user