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(fileExe).copy(tmpFileExe);
|
||||||
QFile(fileBin).copy(tmpFileBin);
|
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{};
|
QString errorText{};
|
||||||
|
|
||||||
auto isGogGalaxyExe = [](QString fileToTest) {
|
auto isGogGalaxyExe = [](QString fileToTest) {
|
||||||
|
@ -258,12 +258,15 @@ void MainWindow::manualInstallFile(QString filePath)
|
|||||||
|
|
||||||
QString fileName = QFileInfo{filePath}.fileName();
|
QString fileName = QFileInfo{filePath}.fileName();
|
||||||
if(filePath.endsWith(".zip", Qt::CaseInsensitive))
|
if(filePath.endsWith(".zip", Qt::CaseInsensitive))
|
||||||
getModView()->downloadFile(fileName.toLower()
|
{
|
||||||
// mod name currently comes from zip file -> remove suffixes from github zip download
|
QString filenameClean = fileName.toLower()
|
||||||
.replace(QRegularExpression("-[0-9a-f]{40}"), "")
|
// mod name currently comes from zip file -> remove suffixes from github zip download
|
||||||
.replace(QRegularExpression("-vcmi-.+\\.zip"), ".zip")
|
.replace(QRegularExpression("-[0-9a-f]{40}"), "")
|
||||||
.replace("-main.zip", ".zip")
|
.replace(QRegularExpression("-vcmi-.+\\.zip"), ".zip")
|
||||||
, QUrl::fromLocalFile(filePath), "mods");
|
.replace("-main.zip", ".zip");
|
||||||
|
|
||||||
|
getModView()->downloadFile(filenameClean, QUrl::fromLocalFile(filePath), "mods");
|
||||||
|
}
|
||||||
else if(filePath.endsWith(".json", Qt::CaseInsensitive))
|
else if(filePath.endsWith(".json", Qt::CaseInsensitive))
|
||||||
{
|
{
|
||||||
QDir configDir(QString::fromStdString(VCMIDirs::get().userConfigPath().string()));
|
QDir configDir(QString::fromStdString(VCMIDirs::get().userConfigPath().string()));
|
||||||
|
@ -41,37 +41,25 @@ void ChroniclesExtractor::removeTempDir()
|
|||||||
tempDir.removeRecursively();
|
tempDir.removeRecursively();
|
||||||
}
|
}
|
||||||
|
|
||||||
int ChroniclesExtractor::getChronicleNo(QFile & file)
|
int ChroniclesExtractor::getChronicleNo()
|
||||||
{
|
{
|
||||||
if(!file.open(QIODevice::ReadOnly))
|
QStringList appDirCandidates = tempDir.entryList({"app"}, QDir::Filter::Dirs);
|
||||||
{
|
|
||||||
QMessageBox::critical(parent, tr("The file cannot be opened"), file.errorString());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray magic{"MZ"};
|
if (!appDirCandidates.empty())
|
||||||
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!"));
|
QDir appDir = tempDir.filePath(appDirCandidates.front());
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray dataBegin = file.read(1'000'000);
|
for (size_t i = 1; i < chronicles.size(); ++i)
|
||||||
int chronicle = 0;
|
|
||||||
for (const auto& kv : chronicles) {
|
|
||||||
if(dataBegin.contains(kv.second))
|
|
||||||
{
|
{
|
||||||
chronicle = kv.first;
|
QString chronicleName = chronicles.at(i);
|
||||||
break;
|
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;
|
||||||
QMessageBox::critical(parent, tr("Invalid file selected"), tr("You have to select a Heroes Chronicles installer file!"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return chronicle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChroniclesExtractor::extractGogInstaller(QString file)
|
bool ChroniclesExtractor::extractGogInstaller(QString file)
|
||||||
@ -147,14 +135,13 @@ void ChroniclesExtractor::createChronicleMod(int no)
|
|||||||
dir.removeRecursively();
|
dir.removeRecursively();
|
||||||
dir.mkpath(".");
|
dir.mkpath(".");
|
||||||
|
|
||||||
QByteArray tmpChronicles = chronicles.at(no);
|
QString tmpChronicles = chronicles.at(no);
|
||||||
tmpChronicles.replace('\0', "");
|
|
||||||
|
|
||||||
QJsonObject mod
|
QJsonObject mod
|
||||||
{
|
{
|
||||||
{ "modType", "Expansion" },
|
{ "modType", "Expansion" },
|
||||||
{ "name", QString::number(no) + " - " + QString(tmpChronicles) },
|
{ "name", QString("%1 - %2").arg(no).arg(tmpChronicles) },
|
||||||
{ "description", tr("Heroes Chronicles") + " - " + QString::number(no) + " - " + QString(tmpChronicles) },
|
{ "description", tr("Heroes Chronicles %1 - %2").arg(no).arg(tmpChronicles) },
|
||||||
{ "author", "3DO" },
|
{ "author", "3DO" },
|
||||||
{ "version", "1.0" },
|
{ "version", "1.0" },
|
||||||
{ "contact", "vcmi.eu" },
|
{ "contact", "vcmi.eu" },
|
||||||
@ -171,8 +158,7 @@ void ChroniclesExtractor::createChronicleMod(int no)
|
|||||||
|
|
||||||
void ChroniclesExtractor::extractFiles(int no) const
|
void ChroniclesExtractor::extractFiles(int no) const
|
||||||
{
|
{
|
||||||
QByteArray tmpChronicles = chronicles.at(no);
|
QString tmpChronicles = chronicles.at(no);
|
||||||
tmpChronicles.replace('\0', "");
|
|
||||||
|
|
||||||
std::string chroniclesDir = "chronicles_" + std::to_string(no);
|
std::string chroniclesDir = "chronicles_" + std::to_string(no);
|
||||||
QDir tmpDir = tempDir.filePath(tempDir.entryList({"app"}, QDir::Filter::Dirs).front());
|
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)
|
void ChroniclesExtractor::installChronicles(QStringList exe)
|
||||||
{
|
{
|
||||||
|
logGlobal->info("Installing Chronicles");
|
||||||
|
|
||||||
extractionFile = -1;
|
extractionFile = -1;
|
||||||
fileCount = exe.size();
|
fileCount = exe.size();
|
||||||
for(QString f : exe)
|
for(QString f : exe)
|
||||||
{
|
{
|
||||||
extractionFile++;
|
extractionFile++;
|
||||||
|
|
||||||
|
logGlobal->info("Creating temporary directory");
|
||||||
if(!createTempDir())
|
if(!createTempDir())
|
||||||
continue;
|
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");
|
QString filepath = tempDir.filePath("chr.exe");
|
||||||
QFile(f).copy(filepath);
|
QFile(f).copy(filepath);
|
||||||
QFile file(filepath);
|
QFile file(filepath);
|
||||||
|
|
||||||
int chronicleNo = getChronicleNo(file);
|
logGlobal->info("Extracting offline installer");
|
||||||
|
if(!extractGogInstaller(filepath))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
logGlobal->info("Detecting Chronicle");
|
||||||
|
int chronicleNo = getChronicleNo();
|
||||||
if(!chronicleNo)
|
if(!chronicleNo)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if(!extractGogInstaller(filepath))
|
logGlobal->info("Creating base Chronicle mod");
|
||||||
continue;
|
|
||||||
|
|
||||||
createBaseMod();
|
createBaseMod();
|
||||||
|
|
||||||
|
logGlobal->info("Creating Chronicle mod");
|
||||||
createChronicleMod(chronicleNo);
|
createChronicleMod(chronicleNo);
|
||||||
|
|
||||||
|
logGlobal->info("Removing temporary directory");
|
||||||
removeTempDir();
|
removeTempDir();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logGlobal->info("Chronicles installed");
|
||||||
}
|
}
|
||||||
|
@ -24,21 +24,22 @@ class ChroniclesExtractor : public QObject
|
|||||||
|
|
||||||
bool createTempDir();
|
bool createTempDir();
|
||||||
void removeTempDir();
|
void removeTempDir();
|
||||||
int getChronicleNo(QFile & file);
|
int getChronicleNo();
|
||||||
bool extractGogInstaller(QString filePath);
|
bool extractGogInstaller(QString filePath);
|
||||||
void createBaseMod() const;
|
void createBaseMod() const;
|
||||||
void createChronicleMod(int no);
|
void createChronicleMod(int no);
|
||||||
void extractFiles(int no) const;
|
void extractFiles(int no) const;
|
||||||
|
|
||||||
const std::map<int, QByteArray> chronicles = {
|
const QStringList chronicles = {
|
||||||
{1, QByteArray{reinterpret_cast<const char*>(u"Warlords of the Wasteland"), 50}},
|
{}, // fake 0th "chronicle", to create 1-based list
|
||||||
{2, QByteArray{reinterpret_cast<const char*>(u"Conquest of the Underworld"), 52}},
|
"Warlords of the Wasteland",
|
||||||
{3, QByteArray{reinterpret_cast<const char*>(u"Masters of the Elements"), 46}},
|
"Conquest of the Underworld",
|
||||||
{4, QByteArray{reinterpret_cast<const char*>(u"Clash of the Dragons"), 40}},
|
"Masters of the Elements",
|
||||||
{5, QByteArray{reinterpret_cast<const char*>(u"The World Tree"), 28}},
|
"Clash of the Dragons",
|
||||||
{6, QByteArray{reinterpret_cast<const char*>(u"The Fiery Moon"), 28}},
|
"The World Tree",
|
||||||
{7, QByteArray{reinterpret_cast<const char*>(u"Revolt of the Beastmasters"), 52}},
|
"The Fiery Moon",
|
||||||
{8, QByteArray{reinterpret_cast<const char*>(u"The Sword of Frost"), 36}}
|
"Revolt of the Beastmasters",
|
||||||
|
"The Sword of Frost",
|
||||||
};
|
};
|
||||||
public:
|
public:
|
||||||
void installChronicles(QStringList exe);
|
void installChronicles(QStringList exe);
|
||||||
|
@ -817,6 +817,8 @@ void CModListView::installFiles(QStringList files)
|
|||||||
{
|
{
|
||||||
ChroniclesExtractor ce(this, [&prog](float progress) { prog = progress; });
|
ChroniclesExtractor ce(this, [&prog](float progress) { prog = progress; });
|
||||||
ce.installChronicles(exe);
|
ce.installChronicles(exe);
|
||||||
|
modStateModel->reloadLocalState();
|
||||||
|
modModel->reloadRepositories();
|
||||||
enableModByName("chronicles");
|
enableModByName("chronicles");
|
||||||
return true;
|
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);
|
QStringList files = QFileDialog::getOpenFileNames(this, tr("Select files (configs, mods, maps, campaigns, gog files) to install..."), QDir::homePath(), filter);
|
||||||
|
|
||||||
for(const auto & file : files)
|
for(const auto & file : files)
|
||||||
|
{
|
||||||
|
logGlobal->info("Importing file %s", file.toStdString());
|
||||||
getMainWindow()->manualInstallFile(file);
|
getMainWindow()->manualInstallFile(file);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// iOS can't display modal dialogs when called directly on button press
|
// 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>
|
<source>Heroes Chronicles</source>
|
||||||
<translation>英雄无敌历代记</translation>
|
<translation>英雄无敌历代记</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||||
|
<source>Heroes Chronicles %1 - %2</source>
|
||||||
|
<translation type="unfinished">英雄无敌历代记 %1 - %2</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>File size</name>
|
<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>
|
<source>Heroes Chronicles</source>
|
||||||
<translation>Heroes Chronicles</translation>
|
<translation>Heroes Chronicles</translation>
|
||||||
</message>
|
</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>
|
||||||
<context>
|
<context>
|
||||||
<name>File size</name>
|
<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>
|
<source>Heroes Chronicles</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||||
|
<source>Heroes Chronicles %1 - %2</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>File size</name>
|
<name>File size</name>
|
||||||
|
@ -1080,6 +1080,11 @@ Exklusiver Vollbildmodus - das Spiel bedeckt den gesamten Bildschirm und verwend
|
|||||||
<source>Heroes Chronicles</source>
|
<source>Heroes Chronicles</source>
|
||||||
<translation>Heroes Chronicles</translation>
|
<translation>Heroes Chronicles</translation>
|
||||||
</message>
|
</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>
|
||||||
<context>
|
<context>
|
||||||
<name>File size</name>
|
<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>
|
<source>Heroes Chronicles</source>
|
||||||
<translation>Heroes Kroniki</translation>
|
<translation>Heroes Kroniki</translation>
|
||||||
</message>
|
</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>
|
||||||
<context>
|
<context>
|
||||||
<name>File size</name>
|
<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>
|
<source>Heroes Chronicles</source>
|
||||||
<translation>Heroes Chronicles</translation>
|
<translation>Heroes Chronicles</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||||
|
<source>Heroes Chronicles %1 - %2</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>File size</name>
|
<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>
|
<source>Heroes Chronicles</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||||
|
<source>Heroes Chronicles %1 - %2</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>File size</name>
|
<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>
|
<source>Heroes Chronicles</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||||
|
<source>Heroes Chronicles %1 - %2</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>File size</name>
|
<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>
|
<source>Heroes Chronicles</source>
|
||||||
<translation>Heroes Chronicles</translation>
|
<translation>Heroes Chronicles</translation>
|
||||||
</message>
|
</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>
|
||||||
<context>
|
<context>
|
||||||
<name>File size</name>
|
<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>
|
<source>Heroes Chronicles</source>
|
||||||
<translation>Хроніки Героїв</translation>
|
<translation>Хроніки Героїв</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||||
|
<source>Heroes Chronicles %1 - %2</source>
|
||||||
|
<translation type="unfinished">Хроніки Героїв %1 - %2</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>File size</name>
|
<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>
|
<source>Heroes Chronicles</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../modManager/chroniclesextractor.cpp" line="144"/>
|
||||||
|
<source>Heroes Chronicles %1 - %2</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>File size</name>
|
<name>File size</name>
|
||||||
|
Loading…
Reference in New Issue
Block a user