1
0
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:
Ivan Savenko 2024-12-17 00:12:57 +02:00 committed by GitHub
commit 604aa9a6b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 120 additions and 50 deletions

View File

@ -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) {

View File

@ -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()));

View File

@ -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");
}

View File

@ -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);

View File

@ -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;
});

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -1055,6 +1055,11 @@ Mode exclusif plein écran - le jeu couvrira l&quot;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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>