2023-01-26 18:24:34 +02:00
/*
* firstlaunch_moc . 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 "firstlaunch_moc.h"
# include "ui_firstlaunch_moc.h"
2023-03-08 02:01:56 +02:00
# include "mainwindow_moc.h"
2023-03-11 00:57:55 +02:00
# include "modManager/cmodlistview_moc.h"
2023-03-08 02:01:56 +02:00
# include "../../lib/CConfigHandler.h"
2023-03-11 00:57:55 +02:00
# include "../../lib/CGeneralTextHandler.h"
# include "../../lib/Languages.h"
# include "../../lib/VCMIDirs.h"
# include "../../lib/filesystem/Filesystem.h"
2024-05-01 20:05:01 +02:00
# include "../helper.h"
2023-03-11 00:57:55 +02:00
# include "../languages.h"
2023-03-08 02:01:56 +02:00
2024-04-16 22:23:41 +02:00
# ifdef ENABLE_INNOEXTRACT
2024-04-01 02:31:33 +02:00
# include "cli/extract.hpp"
2024-06-22 19:18:50 +02:00
# include "setup/version.hpp"
2024-04-01 02:31:33 +02:00
# endif
2024-06-09 17:10:28 +02:00
# ifdef VCMI_IOS
# include "ios/selectdirectory.h"
2024-06-09 17:11:14 +02:00
# include "iOS_utils.h"
2024-06-09 17:10:28 +02:00
# elif defined(VCMI_ANDROID)
2024-05-26 16:38:50 +02:00
# include <QAndroidJniObject>
# include <QtAndroid>
static FirstLaunchView * thiz ;
extern " C " JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_heroesDataUpdate ( JNIEnv * env , jclass cls )
{
thiz - > heroesDataUpdate ( ) ;
}
# endif
2023-01-26 18:24:34 +02:00
FirstLaunchView : : FirstLaunchView ( QWidget * parent )
2023-03-11 00:57:55 +02:00
: QWidget ( parent )
, ui ( new Ui : : FirstLaunchView )
2023-01-26 18:24:34 +02:00
{
ui - > setupUi ( this ) ;
2023-03-08 02:01:56 +02:00
2023-03-11 00:57:55 +02:00
enterSetup ( ) ;
activateTabLanguage ( ) ;
2023-03-12 18:33:29 +02:00
ui - > lineEditDataSystem - > setText ( pathToQString ( boost : : filesystem : : absolute ( VCMIDirs : : get ( ) . dataPaths ( ) . front ( ) ) ) ) ;
ui - > lineEditDataUser - > setText ( pathToQString ( boost : : filesystem : : absolute ( VCMIDirs : : get ( ) . userDataPath ( ) ) ) ) ;
2024-04-01 02:31:33 +02:00
2024-05-01 20:05:01 +02:00
Helper : : enableScrollBySwiping ( ui - > listWidgetLanguage ) ;
2024-06-07 16:23:59 +02:00
# ifdef VCMI_MOBILE
// This directory is not accessible to players without rooting of their device
ui - > lineEditDataSystem - > hide ( ) ;
# endif
2024-04-16 22:23:41 +02:00
# ifndef ENABLE_INNOEXTRACT
2024-04-01 02:31:33 +02:00
ui - > pushButtonGogInstall - > hide ( ) ;
2024-06-07 16:23:59 +02:00
ui - > labelDataGogTitle - > hide ( ) ;
ui - > labelDataGogDescr - > hide ( ) ;
2024-04-01 02:31:33 +02:00
# endif
2023-03-11 00:57:55 +02:00
}
void FirstLaunchView : : on_buttonTabLanguage_clicked ( )
{
activateTabLanguage ( ) ;
}
void FirstLaunchView : : on_buttonTabHeroesData_clicked ( )
{
activateTabHeroesData ( ) ;
}
void FirstLaunchView : : on_buttonTabModPreset_clicked ( )
{
activateTabModPreset ( ) ;
}
void FirstLaunchView : : on_listWidgetLanguage_currentRowChanged ( int currentRow )
{
languageSelected ( ui - > listWidgetLanguage - > item ( currentRow ) - > data ( Qt : : UserRole ) . toString ( ) ) ;
}
void FirstLaunchView : : changeEvent ( QEvent * event )
{
if ( event - > type ( ) = = QEvent : : LanguageChange )
{
ui - > retranslateUi ( this ) ;
2023-03-12 23:33:38 +02:00
Languages : : fillLanguages ( ui - > listWidgetLanguage , false ) ;
2023-03-11 00:57:55 +02:00
}
QWidget : : changeEvent ( event ) ;
}
void FirstLaunchView : : on_pushButtonLanguageNext_clicked ( )
{
activateTabHeroesData ( ) ;
}
void FirstLaunchView : : on_pushButtonDataNext_clicked ( )
{
activateTabModPreset ( ) ;
}
void FirstLaunchView : : on_pushButtonDataBack_clicked ( )
{
activateTabLanguage ( ) ;
}
void FirstLaunchView : : on_pushButtonDataSearch_clicked ( )
{
heroesDataUpdate ( ) ;
}
void FirstLaunchView : : on_pushButtonDataCopy_clicked ( )
{
2024-05-26 16:38:50 +02:00
# ifdef VCMI_ANDROID
thiz = this ;
QtAndroid : : androidActivity ( ) . callMethod < void > ( " copyHeroesData " ) ;
# else
2024-06-08 21:30:54 +02:00
// iOS can't display modal dialogs when called directly on button press
// https://bugreports.qt.io/browse/QTBUG-98651
QTimer : : singleShot ( 0 , this , [ this ] { copyHeroesData ( ) ; } ) ;
2024-05-26 16:38:50 +02:00
# endif
2023-03-11 00:57:55 +02:00
}
2024-04-01 02:31:33 +02:00
void FirstLaunchView : : on_pushButtonGogInstall_clicked ( )
{
2024-06-08 21:30:54 +02:00
// iOS can't display modal dialogs when called directly on button press
// https://bugreports.qt.io/browse/QTBUG-98651
QTimer : : singleShot ( 0 , this , & FirstLaunchView : : extractGogData ) ;
2024-04-01 02:31:33 +02:00
}
2023-03-11 00:57:55 +02:00
void FirstLaunchView : : enterSetup ( )
{
2023-03-12 23:33:38 +02:00
Languages : : fillLanguages ( ui - > listWidgetLanguage , false ) ;
2023-01-26 18:24:34 +02:00
}
2023-03-11 00:57:55 +02:00
void FirstLaunchView : : setSetupProgress ( int progress )
2023-01-26 18:24:34 +02:00
{
2023-06-29 18:09:47 +02:00
ui - > buttonTabLanguage - > setDisabled ( progress < 1 ) ;
ui - > buttonTabHeroesData - > setDisabled ( progress < 2 ) ;
ui - > buttonTabModPreset - > setDisabled ( progress < 3 ) ;
2023-01-26 18:24:34 +02:00
}
2023-03-11 00:57:55 +02:00
void FirstLaunchView : : activateTabLanguage ( )
2023-01-26 18:24:34 +02:00
{
2023-03-11 00:57:55 +02:00
setSetupProgress ( 1 ) ;
2023-01-26 18:24:34 +02:00
ui - > installerTabs - > setCurrentIndex ( 0 ) ;
2023-03-11 00:57:55 +02:00
ui - > buttonTabLanguage - > setChecked ( true ) ;
ui - > buttonTabHeroesData - > setChecked ( false ) ;
ui - > buttonTabModPreset - > setChecked ( false ) ;
2023-01-26 18:24:34 +02:00
}
2023-03-11 00:57:55 +02:00
void FirstLaunchView : : activateTabHeroesData ( )
2023-01-26 18:24:34 +02:00
{
2023-03-11 00:57:55 +02:00
setSetupProgress ( 2 ) ;
2023-01-26 18:24:34 +02:00
ui - > installerTabs - > setCurrentIndex ( 1 ) ;
2023-03-11 00:57:55 +02:00
ui - > buttonTabLanguage - > setChecked ( false ) ;
ui - > buttonTabHeroesData - > setChecked ( true ) ;
ui - > buttonTabModPreset - > setChecked ( false ) ;
2024-01-05 23:03:22 +02:00
if ( heroesDataUpdate ( ) )
2024-06-08 18:38:55 +02:00
{
activateTabModPreset ( ) ;
2024-01-05 23:03:22 +02:00
return ;
2024-06-08 18:38:55 +02:00
}
2024-01-05 23:03:22 +02:00
QString installPath = getHeroesInstallDir ( ) ;
if ( ! installPath . isEmpty ( ) )
2024-01-06 15:50:00 +02:00
{
2024-01-06 23:34:02 +02:00
auto reply = QMessageBox : : question ( this , tr ( " Heroes III installation found! " ) , tr ( " Copy data to VCMI folder? " ) , QMessageBox : : Yes | QMessageBox : : No ) ;
2024-01-06 15:50:00 +02:00
if ( reply = = QMessageBox : : Yes )
copyHeroesData ( installPath ) ;
}
2023-01-26 18:24:34 +02:00
}
2023-03-11 00:57:55 +02:00
void FirstLaunchView : : activateTabModPreset ( )
2023-01-26 18:24:34 +02:00
{
2023-03-11 00:57:55 +02:00
setSetupProgress ( 3 ) ;
2023-01-26 18:24:34 +02:00
ui - > installerTabs - > setCurrentIndex ( 2 ) ;
2023-03-11 00:57:55 +02:00
ui - > buttonTabLanguage - > setChecked ( false ) ;
ui - > buttonTabHeroesData - > setChecked ( false ) ;
ui - > buttonTabModPreset - > setChecked ( true ) ;
modPresetUpdate ( ) ;
2023-01-26 18:24:34 +02:00
}
2023-03-11 00:57:55 +02:00
void FirstLaunchView : : exitSetup ( )
{
2023-10-28 17:44:17 +02:00
if ( auto * mainWindow = dynamic_cast < MainWindow * > ( QApplication : : activeWindow ( ) ) )
2023-03-12 18:33:29 +02:00
mainWindow - > exitSetup ( ) ;
2023-03-11 00:57:55 +02:00
}
// Tab Language
void FirstLaunchView : : languageSelected ( const QString & selectedLanguage )
2023-03-08 02:01:56 +02:00
{
Settings node = settings . write [ " general " ] [ " language " ] ;
node - > String ( ) = selectedLanguage . toStdString ( ) ;
2023-10-28 17:44:17 +02:00
if ( auto * mainWindow = dynamic_cast < MainWindow * > ( QApplication : : activeWindow ( ) ) )
2023-03-08 02:01:56 +02:00
mainWindow - > updateTranslation ( ) ;
}
2024-01-05 23:03:22 +02:00
bool FirstLaunchView : : heroesDataUpdate ( )
2023-03-08 02:01:56 +02:00
{
2024-01-05 23:03:22 +02:00
bool detected = heroesDataDetect ( ) ;
if ( detected )
2023-03-11 00:57:55 +02:00
heroesDataDetected ( ) ;
else
heroesDataMissing ( ) ;
2024-01-05 23:03:22 +02:00
return detected ;
2023-03-11 00:57:55 +02:00
}
void FirstLaunchView : : heroesDataMissing ( )
{
QPalette newPalette = palette ( ) ;
newPalette . setColor ( QPalette : : Base , QColor ( 200 , 50 , 50 ) ) ;
ui - > lineEditDataSystem - > setPalette ( newPalette ) ;
ui - > lineEditDataUser - > setPalette ( newPalette ) ;
2024-06-07 16:23:59 +02:00
ui - > labelDataManualTitle - > setVisible ( true ) ;
ui - > labelDataManualDescr - > setVisible ( true ) ;
2023-03-11 00:57:55 +02:00
ui - > pushButtonDataSearch - > setVisible ( true ) ;
2024-05-26 16:38:50 +02:00
# ifdef VCMI_ANDROID
// selecting directory with ACTION_OPEN_DOCUMENT_TREE is available only since API level 21
2024-06-09 17:11:14 +02:00
const bool canUseDataCopy = QtAndroid : : androidSdkVersion ( ) > = 21 ;
# elif defined(VCMI_IOS)
// selecting directory through UIDocumentPickerViewController is available only since iOS 13
const bool canUseDataCopy = iOS_utils : : isOsVersionAtLeast ( 13 ) ;
2024-06-07 16:23:59 +02:00
# else
2024-06-09 17:11:14 +02:00
const bool canUseDataCopy = true ;
2024-05-26 16:38:50 +02:00
# endif
2023-03-11 00:57:55 +02:00
2024-06-07 16:23:59 +02:00
ui - > labelDataCopyTitle - > setVisible ( canUseDataCopy ) ;
ui - > labelDataCopyDescr - > setVisible ( canUseDataCopy ) ;
ui - > pushButtonDataCopy - > setVisible ( canUseDataCopy ) ;
2023-03-11 00:57:55 +02:00
2024-05-01 12:47:04 +02:00
# ifdef ENABLE_INNOEXTRACT
2024-06-07 16:23:59 +02:00
ui - > pushButtonGogInstall - > setVisible ( true ) ;
ui - > labelDataGogTitle - > setVisible ( true ) ;
ui - > labelDataGogDescr - > setVisible ( true ) ;
2024-05-01 12:47:04 +02:00
# endif
2024-06-07 16:23:59 +02:00
ui - > labelDataFound - > setVisible ( false ) ;
ui - > pushButtonDataNext - > setEnabled ( false ) ;
2023-03-08 02:01:56 +02:00
}
2023-03-11 00:57:55 +02:00
void FirstLaunchView : : heroesDataDetected ( )
{
QPalette newPalette = palette ( ) ;
newPalette . setColor ( QPalette : : Base , QColor ( 50 , 200 , 50 ) ) ;
ui - > lineEditDataSystem - > setPalette ( newPalette ) ;
ui - > lineEditDataUser - > setPalette ( newPalette ) ;
ui - > pushButtonDataSearch - > setVisible ( false ) ;
ui - > pushButtonDataCopy - > setVisible ( false ) ;
2024-06-07 16:23:59 +02:00
ui - > labelDataManualTitle - > setVisible ( false ) ;
ui - > labelDataManualDescr - > setVisible ( false ) ;
ui - > labelDataCopyTitle - > setVisible ( false ) ;
ui - > labelDataCopyDescr - > setVisible ( false ) ;
2023-03-11 00:57:55 +02:00
2024-05-01 12:47:04 +02:00
# ifdef ENABLE_INNOEXTRACT
2024-06-07 16:23:59 +02:00
ui - > pushButtonGogInstall - > setVisible ( false ) ;
ui - > labelDataGogTitle - > setVisible ( false ) ;
ui - > labelDataGogDescr - > setVisible ( false ) ;
2024-05-01 12:47:04 +02:00
# endif
2023-03-11 00:57:55 +02:00
ui - > labelDataFound - > setVisible ( true ) ;
2023-04-15 19:04:00 +02:00
ui - > pushButtonDataNext - > setEnabled ( true ) ;
2023-03-11 00:57:55 +02:00
2024-06-07 16:23:59 +02:00
CGeneralTextHandler : : detectInstallParameters ( ) ;
2023-03-11 00:57:55 +02:00
}
// Tab Heroes III Data
bool FirstLaunchView : : heroesDataDetect ( )
{
// user might have copied files to one of our data path.
// perform full reinitialization of virtual filesystem
CResourceHandler : : destroy ( ) ;
CResourceHandler : : initialize ( ) ;
CResourceHandler : : load ( " config/filesystem.json " ) ;
// use file from lod archive to check presence of H3 data. Very rough estimate, but will work in majority of cases
2023-08-23 14:07:50 +02:00
bool heroesDataFoundROE = CResourceHandler : : get ( ) - > existsResource ( ResourcePath ( " DATA/GENRLTXT.TXT " ) ) ;
bool heroesDataFoundSOD = CResourceHandler : : get ( ) - > existsResource ( ResourcePath ( " DATA/TENTCOLR.TXT " ) ) ;
2023-03-11 00:57:55 +02:00
2023-03-26 22:21:29 +02:00
return heroesDataFoundROE & & heroesDataFoundSOD ;
2023-03-11 00:57:55 +02:00
}
2024-01-05 23:03:22 +02:00
QString FirstLaunchView : : getHeroesInstallDir ( )
{
# ifdef VCMI_WINDOWS
QString gogPath = QSettings ( " HKEY_LOCAL_MACHINE \\ SOFTWARE \\ GOG.com \\ Games \\ 1207658787 " , QSettings : : NativeFormat ) . value ( " path " ) . toString ( ) ;
if ( ! gogPath . isEmpty ( ) )
return gogPath ;
2024-01-06 23:34:02 +02:00
2024-01-05 23:03:22 +02:00
QString cdPath = QSettings ( " HKEY_LOCAL_MACHINE \\ SOFTWARE \\ New World Computing \\ Heroes of Might and Magic® III \\ 1.0 " , QSettings : : NativeFormat ) . value ( " AppPath " ) . toString ( ) ;
if ( ! cdPath . isEmpty ( ) )
return cdPath ;
# endif
2024-01-06 15:50:00 +02:00
return QString { } ;
2024-01-05 23:03:22 +02:00
}
2024-04-01 02:31:33 +02:00
void FirstLaunchView : : extractGogData ( )
{
2024-04-16 22:23:41 +02:00
# ifdef ENABLE_INNOEXTRACT
2024-07-08 18:00:30 +02:00
auto fileSelection = [ this ] ( QByteArray magic , QString filter , QString startPath = { } ) {
2024-04-29 22:05:04 +02:00
QString titleSel = tr ( " Select %1 file... " , " param is file extension " ) . arg ( filter ) ;
QString titleErr = tr ( " You have to select %1 file! " , " param is file extension " ) . arg ( filter ) ;
2024-04-27 23:05:17 +02:00
# if defined(VCMI_MOBILE)
2024-05-01 11:43:20 +02:00
filter = tr ( " GOG file (*.*) " ) ;
2024-04-29 21:09:37 +02:00
QMessageBox : : information ( this , tr ( " File selection " ) , titleSel ) ;
2024-04-27 23:05:17 +02:00
# endif
2024-04-29 21:09:37 +02:00
QString file = QFileDialog : : getOpenFileName ( this , titleSel , startPath . isEmpty ( ) ? QDir : : homePath ( ) : startPath , filter ) ;
if ( file . isEmpty ( ) )
return QString { } ;
2024-07-08 17:27:02 +02:00
QFile tmpFile ( file ) ;
if ( ! tmpFile . open ( QIODevice : : ReadOnly ) )
{
QMessageBox : : critical ( this , tr ( " File cannot opened " ) , titleErr ) ;
return QString { } ;
}
QByteArray magicFile = tmpFile . read ( magic . length ( ) ) ;
2024-07-08 18:00:30 +02:00
if ( ! magicFile . startsWith ( magic ) )
2024-04-29 21:09:37 +02:00
{
QMessageBox : : critical ( this , tr ( " Invalid file selected " ) , titleErr ) ;
return QString { } ;
}
return file ;
} ;
2024-07-08 18:00:30 +02:00
QString fileBin = fileSelection ( QByteArray { " idska32 " } , tr ( " GOG data " ) + " (*.bin) " ) ;
2024-06-22 19:18:50 +02:00
if ( fileBin . isEmpty ( ) )
return ;
2024-07-08 18:00:30 +02:00
QString fileExe = fileSelection ( QByteArray { " MZ " } , tr ( " GOG installer " ) + " (*.exe) " , QFileInfo ( fileBin ) . absolutePath ( ) ) ;
2024-04-01 02:31:33 +02:00
if ( fileExe . isEmpty ( ) )
return ;
2024-06-22 19:18:50 +02:00
2024-05-03 10:37:08 +02:00
ui - > progressBarGog - > setVisible ( true ) ;
ui - > pushButtonGogInstall - > setVisible ( false ) ;
setEnabled ( false ) ;
2024-04-27 22:54:32 +02:00
2024-04-01 02:31:33 +02:00
QTimer : : singleShot ( 100 , this , [ this , fileExe , fileBin ] ( ) { // background to make sure FileDialog is closed...
2024-05-23 01:19:31 +02:00
QDir tempDir ( pathToQString ( VCMIDirs : : get ( ) . userDataPath ( ) ) ) ;
2024-06-22 22:39:40 +02:00
if ( tempDir . cd ( " tmp " ) )
{
tempDir . removeRecursively ( ) ; // remove if already exists (e.g. previous crash)
tempDir . cdUp ( ) ;
}
2024-05-23 01:19:31 +02:00
tempDir . mkdir ( " tmp " ) ;
2024-05-24 23:36:25 +02:00
if ( ! tempDir . cd ( " tmp " ) )
return ; // should not happen - but avoid deleting wrong folder in any case
2024-05-23 01:19:31 +02:00
QString tmpFileExe = tempDir . filePath ( " h3_gog.exe " ) ;
QFile ( fileExe ) . copy ( tmpFileExe ) ;
QFile ( fileBin ) . copy ( tempDir . filePath ( " h3_gog-1.bin " ) ) ;
2024-06-22 21:50:10 +02:00
QString errorText { } ;
auto isGogGalaxyExe = [ ] ( QString fileExe ) {
2024-06-23 12:55:58 +02:00
QFile file ( fileExe ) ;
quint64 fileSize = file . size ( ) ;
2024-06-22 21:50:10 +02:00
if ( fileSize > 10 * 1024 * 1024 )
return false ; // avoid to load big files; galaxy exe is smaller...
2024-06-23 12:55:58 +02:00
if ( ! file . open ( QIODevice : : ReadOnly ) )
return false ;
QByteArray data = file . readAll ( ) ;
2024-06-22 21:50:10 +02:00
2024-06-23 12:55:58 +02:00
const QByteArray magicId { ( const char * ) u " GOG Galaxy " , 20 } ;
return data . contains ( magicId ) ;
2024-06-22 21:50:10 +02:00
} ;
if ( isGogGalaxyExe ( tmpFileExe ) )
errorText = tr ( " You've provided GOG Galaxy installer! This file doesn't contain the game. Please download the offline backup game installer! " ) ;
2024-05-23 01:19:31 +02:00
: : 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 completly
2024-06-22 19:18:50 +02:00
try
{
2024-06-22 21:50:10 +02:00
if ( errorText . isEmpty ( ) )
process_file ( tmpFileExe . toStdString ( ) , o , [ this ] ( float progress ) {
ui - > progressBarGog - > setValue ( progress * 100 ) ;
qApp - > processEvents ( ) ;
} ) ;
2024-06-22 19:18:50 +02:00
}
catch ( const std : : ios_base : : failure & e )
{
errorText = tr ( " Stream error while extracting files! \n error 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! " ) ;
}
2024-05-23 01:19:31 +02:00
ui - > progressBarGog - > setVisible ( false ) ;
ui - > pushButtonGogInstall - > setVisible ( true ) ;
setEnabled ( true ) ;
QStringList dirData = tempDir . entryList ( { " data " } , QDir : : Filter : : Dirs ) ;
2024-06-22 19:18:50 +02:00
if ( ! errorText . isEmpty ( ) | | dirData . empty ( ) | | QDir ( tempDir . filePath ( dirData . front ( ) ) ) . entryList ( { " *.lod " } , QDir : : Filter : : Files ) . empty ( ) )
2024-05-23 01:19:31 +02:00
{
2024-06-22 19:18:50 +02:00
if ( ! errorText . isEmpty ( ) )
QMessageBox : : critical ( this , tr ( " Extracting error! " ) , errorText , QMessageBox : : Ok , QMessageBox : : Ok ) ;
else
QMessageBox : : critical ( this , tr ( " No Heroes III data! " ) , tr ( " Selected files do not contain Heroes III data! " ) , QMessageBox : : Ok , QMessageBox : : Ok ) ;
2024-05-24 23:36:25 +02:00
tempDir . removeRecursively ( ) ;
2024-05-23 01:19:31 +02:00
return ;
2024-04-01 02:31:33 +02:00
}
2024-05-23 01:19:31 +02:00
copyHeroesData ( tempDir . path ( ) , true ) ;
tempDir . removeRecursively ( ) ;
2024-04-01 02:31:33 +02:00
} ) ;
# endif
}
void FirstLaunchView : : copyHeroesData ( const QString & path , bool move )
2023-03-11 00:57:55 +02:00
{
2024-03-02 23:24:00 +02:00
QDir sourceRoot { path } ;
2024-06-09 17:10:28 +02:00
# ifdef VCMI_IOS
// TODO: Qt 6.5 can select directories https://codereview.qt-project.org/c/qt/qtbase/+/446449
SelectDirectory iosDirectorySelector ;
if ( path . isEmpty ( ) )
sourceRoot . setPath ( iosDirectorySelector . getExistingDirectory ( ) ) ;
# else
2024-01-05 23:03:22 +02:00
if ( path . isEmpty ( ) )
2024-01-06 23:34:02 +02:00
sourceRoot . setPath ( QFileDialog : : getExistingDirectory ( this , { } , { } , QFileDialog : : ShowDirsOnly | QFileDialog : : DontResolveSymlinks ) ) ;
2024-06-09 17:10:28 +02:00
# endif
2023-03-11 00:57:55 +02:00
2023-03-12 18:33:29 +02:00
if ( ! sourceRoot . exists ( ) )
2023-03-11 00:57:55 +02:00
return ;
2023-04-15 19:02:59 +02:00
if ( sourceRoot . dirName ( ) . compare ( " data " , Qt : : CaseInsensitive ) = = 0 )
{
// We got Data folder. Possibly user selected "Data" folder of Heroes III install. Check whether valid data might exist 1 level above
QStringList dirData = sourceRoot . entryList ( { " data " } , QDir : : Filter : : Dirs ) ;
if ( dirData . empty ( ) )
{
// This is "Data" folder without any "Data" folders inside. Try to check for data 1 level above
sourceRoot . cdUp ( ) ;
}
}
2023-03-12 18:33:29 +02:00
QStringList dirData = sourceRoot . entryList ( { " data " } , QDir : : Filter : : Dirs ) ;
QStringList dirMaps = sourceRoot . entryList ( { " maps " } , QDir : : Filter : : Dirs ) ;
QStringList dirMp3 = sourceRoot . entryList ( { " mp3 " } , QDir : : Filter : : Dirs ) ;
2023-03-11 00:57:55 +02:00
2024-03-02 23:24:00 +02:00
const auto noDataMessage = tr ( " Failed to detect valid Heroes III data in chosen directory. \n Please select directory with installed Heroes III data. " ) ;
2023-03-26 22:21:29 +02:00
if ( dirData . empty ( ) )
{
2024-03-02 23:24:00 +02:00
QMessageBox : : critical ( this , tr ( " Heroes III data not found! " ) , noDataMessage ) ;
2023-03-11 00:57:55 +02:00
return ;
2023-03-26 22:21:29 +02:00
}
2023-03-11 00:57:55 +02:00
2023-03-12 18:33:29 +02:00
QDir sourceData = sourceRoot . filePath ( dirData . front ( ) ) ;
2023-03-26 22:21:29 +02:00
QStringList roeFiles = sourceData . entryList ( { " *.lod " } , QDir : : Filter : : Files ) ;
QStringList sodFiles = sourceData . entryList ( { " H3ab*.lod " } , QDir : : Filter : : Files ) ;
QStringList hdFiles = sourceData . entryList ( { " *.pak " } , QDir : : Filter : : Files ) ;
2023-03-11 00:57:55 +02:00
2023-03-26 22:21:29 +02:00
if ( sodFiles . empty ( ) )
{
if ( roeFiles . empty ( ) )
{
// Directory structure is correct (Data/Maps/Mp3) but no .lod archives that should be present in any install
2024-03-02 23:24:00 +02:00
QMessageBox : : critical ( this , tr ( " Heroes III data not found! " ) , noDataMessage ) ;
2023-03-26 22:21:29 +02:00
return ;
}
if ( ! hdFiles . empty ( ) )
{
// HD Edition contains only RoE data so we can't use even unmodified files from it
2024-04-20 00:26:58 +02:00
QMessageBox : : critical ( this , tr ( " Heroes III data not found! " ) , tr ( " Heroes III: HD Edition files are not supported by VCMI. \n Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. " ) ) ;
2023-03-26 22:21:29 +02:00
return ;
}
// RoE or some other unsupported edition. Demo version?
2024-04-20 00:26:58 +02:00
QMessageBox : : critical ( this , tr ( " Heroes III data not found! " ) , tr ( " Unknown or unsupported Heroes III version found. \n Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. " ) ) ;
2023-03-11 00:57:55 +02:00
return ;
2023-03-26 22:21:29 +02:00
}
QStringList copyDirectories ;
copyDirectories + = dirData . front ( ) ;
if ( ! dirMaps . empty ( ) )
copyDirectories + = dirMaps . front ( ) ;
2023-03-11 00:57:55 +02:00
2023-03-26 22:21:29 +02:00
if ( ! dirMp3 . empty ( ) )
copyDirectories + = dirMp3 . front ( ) ;
2023-03-11 00:57:55 +02:00
2023-03-12 18:33:29 +02:00
QDir targetRoot = pathToQString ( VCMIDirs : : get ( ) . userDataPath ( ) ) ;
2023-03-11 00:57:55 +02:00
2023-03-12 18:33:29 +02:00
for ( const QString & dirName : copyDirectories )
2023-03-11 00:57:55 +02:00
{
2023-03-12 18:33:29 +02:00
QDir sourceDir = sourceRoot . filePath ( dirName ) ;
2023-03-11 00:57:55 +02:00
QDir targetDir = targetRoot . filePath ( dirName ) ;
if ( ! targetRoot . exists ( dirName ) )
targetRoot . mkdir ( dirName ) ;
2023-03-12 18:33:29 +02:00
for ( const QString & filename : sourceDir . entryList ( QDir : : Filter : : Files ) )
2023-03-11 00:57:55 +02:00
{
QFile sourceFile ( sourceDir . filePath ( filename ) ) ;
2024-04-01 02:31:33 +02:00
if ( move )
sourceFile . rename ( targetDir . filePath ( filename ) ) ;
else
sourceFile . copy ( targetDir . filePath ( filename ) ) ;
2023-03-11 00:57:55 +02:00
}
}
heroesDataUpdate ( ) ;
}
// Tab Mod Preset
void FirstLaunchView : : modPresetUpdate ( )
{
bool translationExists = ! findTranslationModName ( ) . isEmpty ( ) ;
2023-04-03 00:46:26 +02:00
ui - > labelPresetLanguageDescr - > setVisible ( translationExists ) ;
2024-06-07 16:23:59 +02:00
ui - > buttonPresetLanguage - > setVisible ( translationExists ) ;
ui - > buttonPresetLanguage - > setVisible ( checkCanInstallTranslation ( ) ) ;
ui - > buttonPresetExtras - > setVisible ( checkCanInstallExtras ( ) ) ;
ui - > buttonPresetHota - > setVisible ( checkCanInstallHota ( ) ) ;
ui - > buttonPresetWog - > setVisible ( checkCanInstallWog ( ) ) ;
2023-03-11 00:57:55 +02:00
2024-06-07 16:23:59 +02:00
ui - > labelPresetLanguageDescr - > setVisible ( checkCanInstallTranslation ( ) ) ;
ui - > labelPresetExtrasDescr - > setVisible ( checkCanInstallExtras ( ) ) ;
ui - > labelPresetHotaDescr - > setVisible ( checkCanInstallHota ( ) ) ;
ui - > labelPresetWogDescr - > setVisible ( checkCanInstallWog ( ) ) ;
2023-03-26 19:11:59 +02:00
// we can't install anything - either repository checkout is off or all recommended mods are already installed
if ( ! checkCanInstallTranslation ( ) & & ! checkCanInstallExtras ( ) & & ! checkCanInstallHota ( ) & & ! checkCanInstallWog ( ) )
exitSetup ( ) ;
2023-03-11 00:57:55 +02:00
}
QString FirstLaunchView : : findTranslationModName ( )
{
if ( ! getModView ( ) )
return QString ( ) ;
QString preferredlanguage = QString : : fromStdString ( settings [ " general " ] [ " language " ] . String ( ) ) ;
QString installedlanguage = QString : : fromStdString ( settings [ " session " ] [ " language " ] . String ( ) ) ;
if ( preferredlanguage = = installedlanguage )
return QString ( ) ;
return getModView ( ) - > getTranslationModName ( preferredlanguage ) ;
}
bool FirstLaunchView : : checkCanInstallTranslation ( )
{
QString modName = findTranslationModName ( ) ;
if ( modName . isEmpty ( ) )
return false ;
return checkCanInstallMod ( modName ) ;
}
bool FirstLaunchView : : checkCanInstallWog ( )
{
return checkCanInstallMod ( " wake-of-gods " ) ;
}
bool FirstLaunchView : : checkCanInstallHota ( )
{
return checkCanInstallMod ( " hota " ) ;
}
bool FirstLaunchView : : checkCanInstallExtras ( )
{
return checkCanInstallMod ( " vcmi-extras " ) ;
}
CModListView * FirstLaunchView : : getModView ( )
{
2023-10-28 17:44:17 +02:00
auto * mainWindow = dynamic_cast < MainWindow * > ( QApplication : : activeWindow ( ) ) ;
2023-03-11 00:57:55 +02:00
assert ( mainWindow ) ;
if ( ! mainWindow )
return nullptr ;
return mainWindow - > getModView ( ) ;
}
bool FirstLaunchView : : checkCanInstallMod ( const QString & modID )
{
2023-03-26 19:11:59 +02:00
return getModView ( ) & & getModView ( ) - > isModAvailable ( modID ) ;
2023-03-11 00:57:55 +02:00
}
2023-03-12 18:33:29 +02:00
void FirstLaunchView : : on_pushButtonPresetBack_clicked ( )
2023-03-11 00:57:55 +02:00
{
2023-03-12 18:33:29 +02:00
activateTabHeroesData ( ) ;
2023-03-11 00:57:55 +02:00
}
2023-03-12 18:33:29 +02:00
void FirstLaunchView : : on_pushButtonPresetNext_clicked ( )
2023-03-11 00:57:55 +02:00
{
2023-03-12 18:33:29 +02:00
QStringList modsToInstall ;
2023-03-11 00:57:55 +02:00
2024-06-07 16:23:59 +02:00
if ( ui - > buttonPresetLanguage - > isChecked ( ) & & checkCanInstallTranslation ( ) )
2023-03-12 18:33:29 +02:00
modsToInstall . push_back ( findTranslationModName ( ) ) ;
2023-03-11 00:57:55 +02:00
2024-06-07 16:23:59 +02:00
if ( ui - > buttonPresetExtras - > isChecked ( ) & & checkCanInstallExtras ( ) )
2023-03-12 18:33:29 +02:00
modsToInstall . push_back ( " vcmi-extras " ) ;
2023-03-11 00:57:55 +02:00
2024-06-07 16:23:59 +02:00
if ( ui - > buttonPresetWog - > isChecked ( ) & & checkCanInstallWog ( ) )
2023-03-12 18:33:29 +02:00
modsToInstall . push_back ( " wake-of-gods " ) ;
2023-03-11 00:57:55 +02:00
2024-06-07 16:23:59 +02:00
if ( ui - > buttonPresetHota - > isChecked ( ) & & checkCanInstallHota ( ) )
2023-03-12 18:33:29 +02:00
modsToInstall . push_back ( " hota " ) ;
2023-03-11 00:57:55 +02:00
2023-03-12 13:28:09 +02:00
exitSetup ( ) ;
2023-03-12 18:33:29 +02:00
for ( auto const & modName : modsToInstall )
getModView ( ) - > doInstallMod ( modName ) ;
2023-03-11 00:57:55 +02:00
}
2023-03-27 18:35:42 +02:00
void FirstLaunchView : : on_pushButtonDiscord_clicked ( )
{
QDesktopServices : : openUrl ( QUrl ( " https://discord.gg/chBT42V " ) ) ;
}
void FirstLaunchView : : on_pushButtonSlack_clicked ( )
{
QDesktopServices : : openUrl ( QUrl ( " https://slack.vcmi.eu/ " ) ) ;
}
void FirstLaunchView : : on_pushButtonGithub_clicked ( )
{
QDesktopServices : : openUrl ( QUrl ( " https://github.com/vcmi/vcmi " ) ) ;
}