2015-04-04 19:16:15 +05:30
|
|
|
#include "FTBPlugin.h"
|
|
|
|
#include "FTBVersion.h"
|
|
|
|
#include "LegacyFTBInstance.h"
|
|
|
|
#include "OneSixFTBInstance.h"
|
2015-02-09 06:21:14 +05:30
|
|
|
#include <BaseInstance.h>
|
|
|
|
#include <icons/IconList.h>
|
|
|
|
#include <InstanceList.h>
|
|
|
|
#include <minecraft/MinecraftVersionList.h>
|
|
|
|
#include <settings/INISettingsObject.h>
|
2015-04-04 19:16:15 +05:30
|
|
|
#include <pathutils.h>
|
|
|
|
#include "QDebug"
|
|
|
|
#include <QXmlStreamReader>
|
|
|
|
#include <QRegularExpression>
|
|
|
|
|
|
|
|
struct FTBRecord
|
|
|
|
{
|
|
|
|
QString dirName;
|
|
|
|
QString name;
|
|
|
|
QString logo;
|
2015-05-23 02:36:51 +05:30
|
|
|
QString iconKey;
|
2015-04-04 19:16:15 +05:30
|
|
|
QString mcVersion;
|
|
|
|
QString description;
|
|
|
|
QString instanceDir;
|
|
|
|
QString templateDir;
|
|
|
|
bool operator==(const FTBRecord other) const
|
|
|
|
{
|
|
|
|
return instanceDir == other.instanceDir;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
inline uint qHash(FTBRecord record)
|
|
|
|
{
|
|
|
|
return qHash(record.instanceDir);
|
|
|
|
}
|
|
|
|
|
2015-02-02 05:39:28 +05:30
|
|
|
QSet<FTBRecord> discoverFTBInstances(SettingsObjectPtr globalSettings)
|
2015-04-04 19:16:15 +05:30
|
|
|
{
|
|
|
|
QSet<FTBRecord> records;
|
2015-05-22 04:57:59 +05:30
|
|
|
QDir dir = QDir(globalSettings->get("FTBLauncherLocal").toString());
|
2015-02-02 05:39:28 +05:30
|
|
|
QDir dataDir = QDir(globalSettings->get("FTBRoot").toString());
|
2015-04-04 19:16:15 +05:30
|
|
|
if (!dataDir.exists())
|
|
|
|
{
|
|
|
|
qDebug() << "The FTB directory specified does not exist. Please check your settings";
|
|
|
|
return records;
|
|
|
|
}
|
|
|
|
else if (!dir.exists())
|
|
|
|
{
|
|
|
|
qDebug() << "The FTB launcher data directory specified does not exist. Please check "
|
|
|
|
"your settings";
|
|
|
|
return records;
|
|
|
|
}
|
|
|
|
dir.cd("ModPacks");
|
|
|
|
auto allFiles = dir.entryList(QDir::Readable | QDir::Files, QDir::Name);
|
|
|
|
for (auto filename : allFiles)
|
|
|
|
{
|
|
|
|
if (!filename.endsWith(".xml"))
|
|
|
|
continue;
|
|
|
|
auto fpath = dir.absoluteFilePath(filename);
|
|
|
|
QFile f(fpath);
|
|
|
|
qDebug() << "Discovering FTB instances -- " << fpath;
|
|
|
|
if (!f.open(QFile::ReadOnly))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// read the FTB packs XML.
|
|
|
|
QXmlStreamReader reader(&f);
|
|
|
|
while (!reader.atEnd())
|
|
|
|
{
|
|
|
|
switch (reader.readNext())
|
|
|
|
{
|
|
|
|
case QXmlStreamReader::StartElement:
|
|
|
|
{
|
|
|
|
if (reader.name() == "modpack")
|
|
|
|
{
|
|
|
|
QXmlStreamAttributes attrs = reader.attributes();
|
|
|
|
FTBRecord record;
|
|
|
|
record.dirName = attrs.value("dir").toString();
|
|
|
|
record.instanceDir = dataDir.absoluteFilePath(record.dirName);
|
|
|
|
record.templateDir = dir.absoluteFilePath(record.dirName);
|
|
|
|
QDir test(record.instanceDir);
|
|
|
|
qDebug() << dataDir.absolutePath() << record.instanceDir << record.dirName;
|
|
|
|
if (!test.exists())
|
|
|
|
continue;
|
|
|
|
record.name = attrs.value("name").toString();
|
|
|
|
record.logo = attrs.value("logo").toString();
|
2015-05-23 02:36:51 +05:30
|
|
|
QString logo = record.logo;
|
|
|
|
record.iconKey = logo.remove(QRegularExpression("\\..*"));
|
2015-04-04 19:16:15 +05:30
|
|
|
auto customVersions = attrs.value("customMCVersions");
|
|
|
|
if (!customVersions.isNull())
|
|
|
|
{
|
|
|
|
QMap<QString, QString> versionMatcher;
|
|
|
|
QString customVersionsStr = customVersions.toString();
|
|
|
|
QStringList list = customVersionsStr.split(';');
|
|
|
|
for (auto item : list)
|
|
|
|
{
|
|
|
|
auto segment = item.split('^');
|
|
|
|
if (segment.size() != 2)
|
|
|
|
{
|
|
|
|
qCritical() << "FTB: Segment of size < 2 in "
|
|
|
|
<< customVersionsStr;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
versionMatcher[segment[0]] = segment[1];
|
|
|
|
}
|
|
|
|
auto actualVersion = attrs.value("version").toString();
|
|
|
|
if (versionMatcher.contains(actualVersion))
|
|
|
|
{
|
|
|
|
record.mcVersion = versionMatcher[actualVersion];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
record.mcVersion = attrs.value("mcVersion").toString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
record.mcVersion = attrs.value("mcVersion").toString();
|
|
|
|
}
|
|
|
|
record.description = attrs.value("description").toString();
|
|
|
|
records.insert(record);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QXmlStreamReader::EndElement:
|
|
|
|
break;
|
|
|
|
case QXmlStreamReader::Characters:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
f.close();
|
|
|
|
}
|
|
|
|
return records;
|
|
|
|
}
|
|
|
|
|
2015-05-23 02:36:51 +05:30
|
|
|
InstancePtr loadInstance(SettingsObjectPtr globalSettings, QMap<QString, QString> &groupMap, const FTBRecord & record)
|
2015-04-04 19:16:15 +05:30
|
|
|
{
|
|
|
|
InstancePtr inst;
|
|
|
|
|
2015-05-23 02:36:51 +05:30
|
|
|
auto m_settings = std::make_shared<INISettingsObject>(PathCombine(record.instanceDir, "instance.cfg"));
|
2015-04-04 19:16:15 +05:30
|
|
|
m_settings->registerSetting("InstanceType", "Legacy");
|
|
|
|
|
2015-05-23 02:36:51 +05:30
|
|
|
qDebug() << "Loading existing " << record.name;
|
2015-04-04 19:16:15 +05:30
|
|
|
|
2015-05-23 02:36:51 +05:30
|
|
|
QString inst_type = m_settings->get("InstanceType").toString();
|
2015-04-04 19:16:15 +05:30
|
|
|
if (inst_type == "LegacyFTB")
|
|
|
|
{
|
2015-05-23 02:36:51 +05:30
|
|
|
inst.reset(new LegacyFTBInstance(globalSettings, m_settings, record.instanceDir));
|
2015-04-04 19:16:15 +05:30
|
|
|
}
|
|
|
|
else if (inst_type == "OneSixFTB")
|
|
|
|
{
|
2015-05-23 02:36:51 +05:30
|
|
|
inst.reset(new OneSixFTBInstance(globalSettings, m_settings, record.instanceDir));
|
2015-04-04 19:16:15 +05:30
|
|
|
}
|
2015-05-23 02:36:51 +05:30
|
|
|
else
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
2015-05-23 19:37:47 +05:30
|
|
|
qDebug() << "Construction " << record.instanceDir;
|
2015-05-23 02:36:51 +05:30
|
|
|
|
2015-05-23 19:37:47 +05:30
|
|
|
SettingsObject::Lock lock(inst->settings());
|
2015-04-04 19:16:15 +05:30
|
|
|
inst->init();
|
2015-05-23 19:37:47 +05:30
|
|
|
qDebug() << "Init " << record.instanceDir;
|
2015-05-23 02:36:51 +05:30
|
|
|
inst->setGroupInitial("FTB");
|
2015-05-25 11:05:43 +05:30
|
|
|
/**
|
|
|
|
* FIXME: this does not respect the user's preferences. BUT, it would work nicely with the planned pack support
|
|
|
|
* -> instead of changing the user values, change pack values (defaults you can look at and revert to)
|
|
|
|
*/
|
|
|
|
/*
|
2015-05-23 02:36:51 +05:30
|
|
|
inst->setName(record.name);
|
|
|
|
inst->setIconKey(record.iconKey);
|
2015-05-25 11:05:43 +05:30
|
|
|
inst->setNotes(record.description);
|
|
|
|
*/
|
2015-05-23 02:36:51 +05:30
|
|
|
if (inst->intendedVersionId() != record.mcVersion)
|
|
|
|
{
|
|
|
|
inst->setIntendedVersionId(record.mcVersion);
|
|
|
|
}
|
2015-05-23 19:37:47 +05:30
|
|
|
qDebug() << "Post-Process " << record.instanceDir;
|
2015-05-23 02:36:51 +05:30
|
|
|
if (!InstanceList::continueProcessInstance(inst, InstanceList::NoCreateError, record.instanceDir, groupMap))
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
2015-05-23 19:37:47 +05:30
|
|
|
qDebug() << "Final " << record.instanceDir;
|
2015-04-04 19:16:15 +05:30
|
|
|
return inst;
|
|
|
|
}
|
|
|
|
|
2015-05-23 02:36:51 +05:30
|
|
|
InstancePtr createInstance(SettingsObjectPtr globalSettings, QMap<QString, QString> &groupMap, const FTBRecord & record)
|
2015-04-04 19:16:15 +05:30
|
|
|
{
|
2015-05-23 02:36:51 +05:30
|
|
|
QDir rootDir(record.instanceDir);
|
2015-04-04 19:16:15 +05:30
|
|
|
|
|
|
|
InstancePtr inst;
|
|
|
|
|
2015-05-23 02:36:51 +05:30
|
|
|
qDebug() << "Converting " << record.name << " as new.";
|
|
|
|
|
|
|
|
auto mcVersion = std::dynamic_pointer_cast<MinecraftVersion>(ENV.getVersion("net.minecraft", record.mcVersion));
|
|
|
|
if (!mcVersion)
|
2015-04-04 19:16:15 +05:30
|
|
|
{
|
2015-05-23 02:36:51 +05:30
|
|
|
qCritical() << "Can't load instance " << record.instanceDir
|
|
|
|
<< " because minecraft version " << record.mcVersion
|
|
|
|
<< " can't be resolved.";
|
2015-04-04 19:16:15 +05:30
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!rootDir.exists() && !rootDir.mkpath("."))
|
|
|
|
{
|
2015-05-23 02:36:51 +05:30
|
|
|
qCritical() << "Can't create instance folder" << record.instanceDir;
|
2015-04-04 19:16:15 +05:30
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2015-05-23 02:36:51 +05:30
|
|
|
auto m_settings = std::make_shared<INISettingsObject>(PathCombine(record.instanceDir, "instance.cfg"));
|
2015-04-04 19:16:15 +05:30
|
|
|
m_settings->registerSetting("InstanceType", "Legacy");
|
|
|
|
|
2015-05-23 02:36:51 +05:30
|
|
|
if (mcVersion->usesLegacyLauncher())
|
2015-04-04 19:16:15 +05:30
|
|
|
{
|
|
|
|
m_settings->set("InstanceType", "LegacyFTB");
|
2015-05-23 02:36:51 +05:30
|
|
|
inst.reset(new LegacyFTBInstance(globalSettings, m_settings, record.instanceDir));
|
|
|
|
inst->setIntendedVersionId(mcVersion->descriptor());
|
2015-04-04 19:16:15 +05:30
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_settings->set("InstanceType", "OneSixFTB");
|
2015-05-23 02:36:51 +05:30
|
|
|
inst.reset(new OneSixFTBInstance(globalSettings, m_settings, record.instanceDir));
|
|
|
|
inst->setIntendedVersionId(mcVersion->descriptor());
|
2015-04-04 19:16:15 +05:30
|
|
|
inst->init();
|
|
|
|
}
|
2015-05-23 02:36:51 +05:30
|
|
|
inst->setGroupInitial("FTB");
|
|
|
|
inst->setName(record.name);
|
2015-05-24 23:18:22 +05:30
|
|
|
inst->setIconKey(record.iconKey);
|
2015-05-23 02:36:51 +05:30
|
|
|
inst->setIntendedVersionId(record.mcVersion);
|
|
|
|
inst->setNotes(record.description);
|
2015-05-23 19:37:47 +05:30
|
|
|
qDebug() << "Post-Process " << record.instanceDir;
|
2015-05-23 02:36:51 +05:30
|
|
|
if (!InstanceList::continueProcessInstance(inst, InstanceList::NoCreateError, record.instanceDir, groupMap))
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2015-04-04 19:16:15 +05:30
|
|
|
return inst;
|
|
|
|
}
|
|
|
|
|
2015-02-02 05:39:28 +05:30
|
|
|
void FTBPlugin::loadInstances(SettingsObjectPtr globalSettings, QMap<QString, QString> &groupMap, QList<InstancePtr> &tempList)
|
2015-04-04 19:16:15 +05:30
|
|
|
{
|
|
|
|
// nothing to load when we don't have
|
2015-02-02 05:39:28 +05:30
|
|
|
if (globalSettings->get("TrackFTBInstances").toBool() != true)
|
2015-04-04 19:16:15 +05:30
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-02-02 05:39:28 +05:30
|
|
|
auto records = discoverFTBInstances(globalSettings);
|
2015-04-04 19:16:15 +05:30
|
|
|
if (!records.size())
|
|
|
|
{
|
|
|
|
qDebug() << "No FTB instances to load.";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
qDebug() << "Loading FTB instances! -- got " << records.size();
|
|
|
|
// process the records we acquired.
|
|
|
|
for (auto record : records)
|
|
|
|
{
|
|
|
|
qDebug() << "Loading FTB instance from " << record.instanceDir;
|
2015-05-23 02:36:51 +05:30
|
|
|
QString iconKey = record.iconKey;
|
|
|
|
ENV.icons()->addIcon(iconKey, iconKey, PathCombine(record.templateDir, record.logo), MMCIcon::Transient);
|
|
|
|
auto settingsFilePath = PathCombine(record.instanceDir, "instance.cfg");
|
2015-05-23 19:37:47 +05:30
|
|
|
qDebug() << "ICON get!";
|
2015-04-04 19:16:15 +05:30
|
|
|
|
2015-05-23 02:36:51 +05:30
|
|
|
if (QFileInfo(settingsFilePath).exists())
|
2015-04-04 19:16:15 +05:30
|
|
|
{
|
2015-05-23 02:36:51 +05:30
|
|
|
auto instPtr = loadInstance(globalSettings, groupMap, record);
|
|
|
|
if (!instPtr)
|
2015-04-04 19:16:15 +05:30
|
|
|
{
|
2015-05-23 02:36:51 +05:30
|
|
|
qWarning() << "Couldn't load instance config:" << settingsFilePath;
|
|
|
|
if(!QFile::remove(settingsFilePath))
|
|
|
|
{
|
|
|
|
qWarning() << "Couldn't remove broken instance config!";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// failed to load, but removed the poisonous file
|
2015-04-04 19:16:15 +05:30
|
|
|
}
|
2015-05-23 02:36:51 +05:30
|
|
|
else
|
2015-04-04 19:16:15 +05:30
|
|
|
{
|
2015-05-23 02:36:51 +05:30
|
|
|
tempList.append(InstancePtr(instPtr));
|
2015-04-04 19:16:15 +05:30
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2015-05-23 02:36:51 +05:30
|
|
|
auto instPtr = createInstance(globalSettings, groupMap, record);
|
|
|
|
if (!instPtr)
|
2015-04-04 19:16:15 +05:30
|
|
|
{
|
2015-05-23 02:36:51 +05:30
|
|
|
qWarning() << "Couldn't create FTB instance!";
|
|
|
|
continue;
|
2015-04-04 19:16:15 +05:30
|
|
|
}
|
2015-05-23 02:36:51 +05:30
|
|
|
tempList.append(InstancePtr(instPtr));
|
2015-04-04 19:16:15 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef Q_OS_WIN32
|
|
|
|
#include <windows.h>
|
|
|
|
static const int APPDATA_BUFFER_SIZE = 1024;
|
|
|
|
#endif
|
|
|
|
|
2015-05-22 04:57:59 +05:30
|
|
|
static QString getLocalCacheStorageLocation()
|
2015-04-04 19:16:15 +05:30
|
|
|
{
|
2015-05-22 04:26:41 +05:30
|
|
|
QString ftbDefault;
|
|
|
|
#ifdef Q_OS_WIN32
|
2015-04-04 19:16:15 +05:30
|
|
|
wchar_t buf[APPDATA_BUFFER_SIZE];
|
2015-05-22 04:26:41 +05:30
|
|
|
if (GetEnvironmentVariableW(L"LOCALAPPDATA", buf, APPDATA_BUFFER_SIZE)) // local
|
2015-04-04 19:16:15 +05:30
|
|
|
{
|
2015-05-22 04:26:41 +05:30
|
|
|
ftbDefault = QDir(QString::fromWCharArray(buf)).absoluteFilePath("ftblauncher");
|
2015-04-04 19:16:15 +05:30
|
|
|
}
|
2015-05-22 04:26:41 +05:30
|
|
|
else if (GetEnvironmentVariableW(L"APPDATA", buf, APPDATA_BUFFER_SIZE)) // roaming
|
2015-04-04 19:16:15 +05:30
|
|
|
{
|
2015-05-22 04:26:41 +05:30
|
|
|
ftbDefault = QDir(QString::fromWCharArray(buf)).absoluteFilePath("ftblauncher");
|
2015-04-04 19:16:15 +05:30
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-05-22 04:26:41 +05:30
|
|
|
qCritical() << "Your LOCALAPPDATA and APPDATA folders are missing!"
|
|
|
|
" If you are on windows, this means your system is broken.";
|
2015-04-04 19:16:15 +05:30
|
|
|
}
|
2015-05-22 04:26:41 +05:30
|
|
|
#elif defined(Q_OS_MAC)
|
|
|
|
ftbDefault = PathCombine(QDir::homePath(), "Library/Application Support/ftblauncher");
|
|
|
|
#else
|
|
|
|
ftbDefault = QDir::home().absoluteFilePath(".ftblauncher");
|
|
|
|
#endif
|
|
|
|
return ftbDefault;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-05-22 04:57:59 +05:30
|
|
|
static QString getRoamingStorageLocation()
|
2015-05-22 04:26:41 +05:30
|
|
|
{
|
|
|
|
QString ftbDefault;
|
|
|
|
#ifdef Q_OS_WIN32
|
|
|
|
wchar_t buf[APPDATA_BUFFER_SIZE];
|
|
|
|
QString cacheStorage;
|
|
|
|
if (GetEnvironmentVariableW(L"APPDATA", buf, APPDATA_BUFFER_SIZE))
|
2015-04-04 19:16:15 +05:30
|
|
|
{
|
2015-05-22 04:26:41 +05:30
|
|
|
ftbDefault = QDir(QString::fromWCharArray(buf)).absoluteFilePath("ftblauncher");
|
2015-04-04 19:16:15 +05:30
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-05-22 04:26:41 +05:30
|
|
|
qCritical() << "Your APPDATA folder is missing! If you are on windows, this means your system is broken.";
|
2015-04-04 19:16:15 +05:30
|
|
|
}
|
|
|
|
#elif defined(Q_OS_MAC)
|
2015-05-22 04:26:41 +05:30
|
|
|
ftbDefault = PathCombine(QDir::homePath(), "Library/Application Support/ftblauncher");
|
|
|
|
#else
|
|
|
|
ftbDefault = QDir::home().absoluteFilePath(".ftblauncher");
|
2015-04-04 19:16:15 +05:30
|
|
|
#endif
|
2015-05-22 04:26:41 +05:30
|
|
|
return ftbDefault;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FTBPlugin::initialize(SettingsObjectPtr globalSettings)
|
|
|
|
{
|
|
|
|
// FTB
|
|
|
|
globalSettings->registerSetting("TrackFTBInstances", false);
|
2015-05-22 04:57:59 +05:30
|
|
|
QString ftbRoaming = getRoamingStorageLocation();
|
|
|
|
QString ftbLocal = getLocalCacheStorageLocation();
|
2015-05-22 04:26:41 +05:30
|
|
|
|
2015-05-22 04:57:59 +05:30
|
|
|
globalSettings->registerSetting("FTBLauncherRoaming", ftbRoaming);
|
|
|
|
globalSettings->registerSetting("FTBLauncherLocal", ftbLocal);
|
|
|
|
qDebug() << "FTB Launcher paths:" << globalSettings->get("FTBLauncherRoaming").toString()
|
|
|
|
<< "and" << globalSettings->get("FTBLauncherLocal").toString();
|
2015-04-04 19:16:15 +05:30
|
|
|
|
2015-02-02 05:39:28 +05:30
|
|
|
globalSettings->registerSetting("FTBRoot");
|
|
|
|
if (globalSettings->get("FTBRoot").isNull())
|
2015-04-04 19:16:15 +05:30
|
|
|
{
|
|
|
|
QString ftbRoot;
|
2015-05-22 04:57:59 +05:30
|
|
|
QFile f(QDir(globalSettings->get("FTBLauncherRoaming").toString()).absoluteFilePath("ftblaunch.cfg"));
|
2015-04-04 19:16:15 +05:30
|
|
|
qDebug() << "Attempting to read" << f.fileName();
|
|
|
|
if (f.open(QFile::ReadOnly))
|
|
|
|
{
|
|
|
|
const QString data = QString::fromLatin1(f.readAll());
|
|
|
|
QRegularExpression exp("installPath=(.*)");
|
|
|
|
ftbRoot = QDir::cleanPath(exp.match(data).captured(1));
|
|
|
|
#ifdef Q_OS_WIN32
|
|
|
|
if (!ftbRoot.isEmpty())
|
|
|
|
{
|
|
|
|
if (ftbRoot.at(0).isLetter() && ftbRoot.size() > 1 && ftbRoot.at(1) == '/')
|
|
|
|
{
|
|
|
|
ftbRoot.remove(1, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (ftbRoot.isEmpty())
|
|
|
|
{
|
|
|
|
qDebug() << "Failed to get FTB root path";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
qDebug() << "FTB is installed at" << ftbRoot;
|
2015-02-02 05:39:28 +05:30
|
|
|
globalSettings->set("FTBRoot", ftbRoot);
|
2015-04-04 19:16:15 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
qWarning() << "Couldn't open" << f.fileName() << ":" << f.errorString();
|
|
|
|
qWarning() << "This is perfectly normal if you don't have FTB installed";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|