Refactor shared X-Plane model parser in common header only functions

ref T290
This commit is contained in:
Roland Winklmeier
2018-08-21 15:04:05 +02:00
parent 597283dde1
commit a19ccabf35
8 changed files with 204 additions and 367 deletions

View File

@@ -18,6 +18,7 @@
#include "blackmisc/simulation/distributor.h"
#include "blackmisc/simulation/xplane/aircraftmodelloaderxplane.h"
#include "blackmisc/simulation/xplane/xplaneutil.h"
#include "blackmisc/simulation/xplane/qtfreeutils.h"
#include "blackmisc/statusmessage.h"
#include "blackmisc/stringutils.h"
#include "blackmisc/worker.h"
@@ -62,30 +63,6 @@ namespace BlackMisc
}
}
//! Create a unique string to identify a model
static QString stringForFlyableModel(const CAircraftModel &model, const QFileInfo &acfFile)
{
if (model.getDistributor().hasDescription())
{
if (!model.getName().isEmpty())
{
if (model.getName().contains(model.getDistributor().getDescription()))
{
return model.getName();
}
else
{
return model.getDistributor().getDescription() % ' ' % model.getName();
}
}
else if (model.hasAircraftDesignator())
{
return model.getDistributor().getDescription() % ' ' % model.getAircraftIcaoCodeDesignator();
}
}
return acfFile.dir().dirName() % ' ' % acfFile.baseName();
}
//! Create a description string for a model that doesn't already have one
static QString descriptionForFlyableModel(const CAircraftModel &model)
{
@@ -217,7 +194,17 @@ namespace BlackMisc
aircraftIt.next();
if (CFileUtils::isExcludedDirectory(aircraftIt.fileInfo(), excludeDirectories, Qt::CaseInsensitive)) { continue; }
CAircraftModel model = this->extractAcfProperties(aircraftIt.filePath(), aircraftIt.fileInfo());
using namespace BlackMisc::Simulation::XPlane::QtFreeUtils;
AcfProperties acfProperties = extractAcfProperties(aircraftIt.filePath().toStdString());
const CDistributor dist({}, QString::fromStdString(acfProperties.author), {}, {}, CSimulatorInfo::XPLANE);
CAircraftModel model;
model.setAircraftIcaoCode(QString::fromStdString(acfProperties.aircraftIcaoCode));
model.setDescription(QString::fromStdString(acfProperties.modelDescription));
model.setName(QString::fromStdString(acfProperties.modelName));
model.setDistributor(dist);
model.setModelString(QString::fromStdString(acfProperties.modelString));
if (!model.hasDescription()) { model.setDescription(descriptionForFlyableModel(model)); }
model.setModelType(CAircraftModel::TypeOwnSimulatorModel);
model.setSimulator(CSimulatorInfo::xplane());
model.setFileDetailsAndTimestamp(aircraftIt.fileInfo());
@@ -236,59 +223,6 @@ namespace BlackMisc
return installedModels;
}
CAircraftModel CAircraftModelLoaderXPlane::extractAcfProperties(const QString &filePath, const QFileInfo &fileInfo)
{
BlackMisc::Simulation::CAircraftModel model;
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { return model; }
QTextStream ts(&file);
if (ts.readLine() == "I" && ts.readLine().contains("version") && ts.readLine() == "ACF")
{
while (!ts.atEnd())
{
const QString line = ts.readLine();
QVector<QStringRef> tokens = line.splitRef(' ', QString::SkipEmptyParts);
if (tokens.size() < 3 || tokens.at(0) != QLatin1String("P")) { continue; }
if (tokens.at(1) == QLatin1String("acf/_ICAO"))
{
const CAircraftIcaoCode icao(tokens.at(2).toString());
model.setAircraftIcaoCode(icao);
}
else if (tokens.at(1) == QLatin1String("acf/_descrip"))
{
const QString desc(line.mid(tokens.at(2).position()));
model.setDescription("[ACF] " % desc);
}
else if (tokens.at(1) == QLatin1String("acf/_name"))
{
const QString name(line.mid(tokens.at(2).position()));
model.setName(name);
}
else if (tokens.at(1) == QLatin1String("acf/_studio"))
{
const CDistributor dist({}, line.mid(tokens.at(2).position()), {}, {}, CSimulatorInfo::XPLANE);
model.setDistributor(dist);
}
else if (tokens.at(1) == QLatin1String("acf/_author"))
{
if (model.getDistributor().hasDescription()) { continue; }
thread_local const QRegularExpression end("\\W\\s", QRegularExpression::UseUnicodePropertiesOption);
QString author = line.mid(tokens.at(2).position());
author = author.left(author.indexOf(end)).trimmed();
if (author.isEmpty()) { continue; }
const CDistributor dist({}, author, {}, {}, CSimulatorInfo::XPLANE);
model.setDistributor(dist);
}
}
}
file.close();
model.setModelString(stringForFlyableModel(model, fileInfo));
if (!model.hasDescription()) { model.setDescription(descriptionForFlyableModel(model)); }
return model;
}
CAircraftModelList CAircraftModelLoaderXPlane::parseCslPackages(const QString &rootDirectory, const QStringList &excludeDirectories)
{
Q_UNUSED(excludeDirectories);

View File

@@ -54,9 +54,6 @@ namespace BlackMisc
//! Parsed or injected models
void updateInstalledModels(const CAircraftModelList &models);
//! Extract from an acf file (flyable plane) the properties needed to generate model string.
static CAircraftModel extractAcfProperties(const QString &filePath, const QFileInfo &fileInfo);
protected:
//! \name Interface functions
//! @{

View File

@@ -0,0 +1,182 @@
/* Copyright (C) 2018
* swift Project Community / Contributors
*
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
* including this file, may be copied, modified, propagated, or distributed except according to the terms
* contained in the LICENSE file.
*/
//! \file
#ifndef BLACKMISC_SIMULATION_XPLANE_XPLANQTFREEUTILS_H
#define BLACKMISC_SIMULATION_XPLANE_XPLANQTFREEUTILS_H
#include <string>
#include <fstream>
#include <vector>
// Strict header only X-Plane model parser utils shared between BlackMisc and XSwiftBus.
// Header only is necessary to no require XSwiftBus to link against BlackMisc.
namespace BlackMisc
{
namespace Simulation
{
namespace XPlane
{
namespace QtFreeUtils
{
//! Get filename (including all extensions) from a filePath
std::string getFileName(const std::string &filePath)
{
const std::string seperator = "/\\";
const std::size_t sepPos = filePath.find_last_of(seperator);
if (sepPos != std::string::npos)
{
return filePath.substr(sepPos + 1, filePath.size() - 1);
}
else
{
return filePath;
}
}
//! Get the name of the parent directory
std::string getDirName(const std::string &filePath)
{
const std::string seperator = "/\\";
const std::size_t sepPos = filePath.find_last_of(seperator);
if (sepPos != std::string::npos)
{
std::string dirPath = filePath.substr(0, sepPos);
return getFileName(dirPath);
}
else
{
return {};
}
}
//! Get the base name of the file
std::string getBaseName(const std::string &filePath)
{
const std::string seperator = ".";
const std::string fileName = getFileName(filePath);
std::size_t sepPos = fileName.find(seperator);
if (sepPos != std::string::npos)
{
return fileName.substr(0, sepPos);
}
else
{
return fileName;
}
}
//! Split string by delimiter and maxSplitCount times
std::vector<std::string> split(const std::string &str, size_t maxSplitCount = 0, const std::string &delimiter = " ")
{
std::string s(str);
size_t pos = 0;
std::vector<std::string> tokens;
while ((pos = s.find(delimiter)) != std::string::npos)
{
tokens.push_back(s.substr(0, pos));
s.erase(0, pos + delimiter.length());
if (maxSplitCount > 0 && tokens.size() == maxSplitCount) { break; }
}
tokens.push_back(s);
return tokens;
}
//! ACF properties
struct AcfProperties
{
std::string aircraftIcaoCode; //!< Aircraft ICAO code
std::string modelDescription; //!< Model description
std::string modelName; //!< Model name
std::string author; //!< Model author
std::string modelString; //!< Generated model string
};
//! Get the model string for a flyable aircraft
std::string stringForFlyableModel(const AcfProperties &acfProperties, const std::string &acfFile)
{
if (! acfProperties.author.empty())
{
if (! acfProperties.modelName.empty())
{
if (acfProperties.modelName.find(acfProperties.author) != std::string::npos) { return acfProperties.modelName; }
else { return acfProperties.author + ' ' + acfProperties.modelName; }
}
else if (! acfProperties.aircraftIcaoCode.empty())
{
return acfProperties.author + ' ' + acfProperties.aircraftIcaoCode;
}
}
return getDirName(acfFile) + ' ' + getBaseName(acfFile);
}
//! Extract ACF properties from an aircraft file
AcfProperties extractAcfProperties(const std::string &filePath)
{
std::ifstream fs(filePath, std::ios::in);
if (!fs.is_open()) { return {}; }
std::string i;
std::string version;
std::string acf;
std::getline(fs, i);
std::getline(fs, version);
std::getline(fs, acf);
AcfProperties acfProperties;
if (i == "I" && version.find("version") != std::string::npos && acf == "ACF")
{
std::string line;
while (std::getline(fs, line))
{
auto tokens = split(line, 2);
if (tokens.size() < 3 || tokens.at(0) != "P") { continue; }
if (tokens.at(1) == "acf/_ICAO")
{
acfProperties.aircraftIcaoCode = tokens.at(2);
}
else if (tokens.at(1) == "acf/_descrip")
{
acfProperties.modelDescription = "[ACF] " + tokens.at(2);
}
else if (tokens.at(1) == "acf/_name")
{
acfProperties.modelName = tokens.at(2);
}
else if (tokens.at(1) == "acf/_studio")
{
acfProperties.author = tokens.at(2);
}
else if (tokens.at(1) == "acf/_author")
{
if (!acfProperties.author.empty()) { continue; }
std::string author = tokens.at(2);
size_t pos = author.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_");
author = author.substr(0, pos);
if (author.empty()) { continue; }
acfProperties.author = author;
}
}
}
fs.close();
acfProperties.modelString = stringForFlyableModel(acfProperties, filePath);
return acfProperties;
}
}
} // namespace
} // namespace
} // namespace
#endif // guard