Added Settings class to VCamUtils.

This commit is contained in:
Gonzalo Exequiel Pedone 2020-08-22 17:35:12 -03:00
parent e8da748d30
commit 39f383560c
No known key found for this signature in database
GPG key ID: B8B09E63E9B85BAF
3 changed files with 543 additions and 0 deletions

View file

@ -43,6 +43,7 @@ SOURCES += \
src/image/videoformat.cpp \
src/image/videoframe.cpp \
src/logger/logger.cpp \
src/settings.cpp \
src/timer.cpp \
src/utils.cpp
@ -55,6 +56,7 @@ HEADERS += \
src/image/videoformattypes.h \
src/ipcbridge.h \
src/logger/logger.h \
src/settings.h \
src/timer.h \
src/utils.h

482
VCamUtils/src/settings.cpp Normal file
View file

@ -0,0 +1,482 @@
/* akvirtualcamera, virtual camera for Mac and Windows.
* Copyright (C) 2020 Gonzalo Exequiel Pedone
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <algorithm>
#include <cstring>
#include <fstream>
#include <map>
#include <sstream>
#include "settings.h"
#include "fraction.h"
#include "logger/logger.h"
namespace AkVCam
{
using GroupMap = std::map<std::string, std::string>;
struct SettingsElement
{
std::string group;
std::string key;
std::string value;
inline bool empty();
};
class SettingsPrivate
{
public:
std::map<std::string, GroupMap> m_configs;
std::string m_currentGroup;
std::string m_currentArray;
size_t m_arrayIndex;
SettingsElement parse(const std::string &line, bool *ok);
std::string parseString(const std::string &str);
GroupMap groupConfigs();
};
}
AkVCam::Settings::Settings()
{
this->d = new SettingsPrivate;
}
AkVCam::Settings::~Settings()
{
this->endArray();
this->endGroup();
delete this->d;
}
bool AkVCam::Settings::load(const std::string &fileName)
{
this->clear();
if (fileName.empty()) {
AkLogError() << "Settings file name not valid" << std::endl;
return false;
}
std::ifstream configFile(fileName);
if (!configFile.is_open()) {
AkLogError() << "Can't open settings file: " << fileName << std::endl;
return false;
}
std::string currentGroup;
while (!configFile.eof()) {
std::string line;
std::getline(configFile, line);
bool ok = false;
auto element = this->d->parse(line, &ok);
if (!ok) {
AkLogError() << "Error parsing settings file: " << fileName << std::endl;
AkLogError() << "Line: " << line << std::endl;
this->clear();
configFile.close();
return false;
}
if (!element.empty()) {
if (!element.group.empty()) {
currentGroup = element.group;
if (this->d->m_configs.count(currentGroup) < 1)
this->d->m_configs[currentGroup] = {};
} else if (!element.key.empty() && !element.value.empty()) {
if (currentGroup.empty()) {
currentGroup = "General";
this->d->m_configs[currentGroup] = {};
}
this->d->m_configs[currentGroup][element.key] = element.value;
}
}
}
configFile.close();
return true;
}
void AkVCam::Settings::beginGroup(const std::string &prefix)
{
this->endGroup();
this->d->m_currentGroup = prefix;
}
void AkVCam::Settings::endGroup()
{
this->d->m_currentGroup = {};
}
size_t AkVCam::Settings::beginArray(const std::string &prefix)
{
auto groupConfigs = this->d->groupConfigs();
if (groupConfigs.empty())
return 0;
this->endArray();
// Read array size.
this->d->m_currentArray = prefix;
auto arraySizeStr = groupConfigs[prefix + "/size"];
char *p = nullptr;
return strtoul(arraySizeStr.c_str(), &p, 10);
}
void AkVCam::Settings::setArrayIndex(size_t i)
{
this->d->m_arrayIndex = i;
}
void AkVCam::Settings::endArray()
{
this->d->m_currentArray = {};
}
std::vector<std::string> AkVCam::Settings::groups() const
{
std::vector<std::string> groups;
for (auto it = this->d->m_configs.begin();
it != this->d->m_configs.end();
it++) {
groups.push_back(it->first);
}
return groups;
}
std::vector<std::string> AkVCam::Settings::keys() const
{
std::vector<std::string> keys;
auto groupConfigs = this->d->groupConfigs();
for (auto it = groupConfigs.begin();
it != groupConfigs.end();
it++) {
keys.push_back(it->first);
}
return keys;
}
void AkVCam::Settings::clear()
{
this->d->m_configs.clear();
this->endArray();
this->endGroup();
this->d->m_arrayIndex = 0;
}
bool AkVCam::Settings::contains(const std::string &key) const
{
if (key.empty())
return false;
auto groupConfigs = this->d->groupConfigs();
bool contains;
if (groupConfigs.empty())
return false;
if (this->d->m_currentArray.empty()) {
contains = groupConfigs.count(key) > 0;
} else {
std::string arrayKey;
std::stringstream ss(arrayKey);
ss << this->d->m_currentArray
<< '/'
<< this->d->m_arrayIndex + 1
<< '/'
<< key;
contains = groupConfigs.count(arrayKey) > 0;
}
return contains;
}
std::string AkVCam::Settings::value(const std::string &key) const
{
if (key.empty())
return {};
auto groupConfigs = this->d->groupConfigs();
std::string value;
if (groupConfigs.empty())
return {};
if (this->d->m_currentArray.empty()) {
value = groupConfigs[key];
} else {
std::string arrayKey;
std::stringstream ss(arrayKey);
ss << this->d->m_currentArray
<< '/'
<< this->d->m_arrayIndex + 1
<< '/'
<< key;
value = groupConfigs[arrayKey];
}
return value;
}
bool AkVCam::Settings::valueBool(const std::string &key) const
{
auto value = this->value(key);
if (value.empty())
return false;
if (value == "true")
return true;
char *p = nullptr;
return strtol(value.c_str(), &p, 10) != 0;
}
int32_t AkVCam::Settings::valueInt32(const std::string &key) const
{
auto value = this->value(key);
if (value.empty())
return false;
char *p = nullptr;
return strtol(value.c_str(), &p, 10);
}
uint32_t AkVCam::Settings::valueUInt32(const std::string &key) const
{
auto value = this->value(key);
if (value.empty())
return false;
char *p = nullptr;
return strtoul(value.c_str(), &p, 10);
}
std::vector<std::string> AkVCam::Settings::valueList(const std::string &key,
const std::string &separators) const
{
auto value = this->value(key);
if (value.empty())
return {};
char *valuePtr = const_cast<char *>(value.c_str());
std::vector<std::string> result;
for (;;) {
auto element = strsep(&valuePtr, separators.c_str());
if (!element)
break;
result.push_back(trimmed(element));
}
return result;
}
AkVCam::Fraction AkVCam::Settings::valueFrac(const std::string &key) const
{
auto fracList = this->valueList(key, "/");
if (fracList.empty())
return {};
Fraction frac(0, 1);
char *p = nullptr;
switch (fracList.size()) {
case 1:
frac.num() = strtoul(fracList[0].c_str(), &p, 10);
break;
case 2:
frac.num() = strtoul(fracList[0].c_str(), &p, 10);
p = nullptr;
frac.den() = strtoul(fracList[1].c_str(), &p, 10);
if (frac.den() < 1) {
frac.num() = 0;
frac.den() = 1;
}
break;
default:
break;
}
return frac;
}
AkVCam::SettingsElement AkVCam::SettingsPrivate::parse(const std::string &line, bool *ok)
{
SettingsElement element;
if (line.empty() || line[0] == '#' || line[0] == ';') {
if (ok)
*ok = false;
return {};
}
if (line[0] == '[') {
if (line[line.size() - 1] != ']' || line.size() < 3) {
if (ok)
*ok = false;
return {};
}
element.group = trimmed(line.substr(1, line.size() - 2));
if (ok)
*ok = true;
return element;
}
auto pairSep = strchr(line.c_str(), '=');
if (!pairSep) {
if (ok)
*ok = false;
return {};
}
element.key = trimmed(line.substr(0, (size_t) (pairSep - line.c_str())));
std::replace(element.key.begin(), element.key.end(), '\\', '/');
if (element.key.empty()) {
if (ok)
*ok = false;
return {};
}
auto offset = (size_t) (pairSep - line.c_str() + 1);
element.value = trimmed(line.substr(offset, line.size() - offset));
element.value = this->parseString(element.value);
if (ok)
*ok = true;
return element;
}
/* Escape sequences taken from:
*
* https://en.cppreference.com/w/cpp/language/escape
*
* but we ignore octals and universal characters.
*/
std::string AkVCam::SettingsPrivate::parseString(const std::string &str)
{
if (str.size() < 2)
return str;
const char escape_k[] = "'\"?\\abfnrtv0";
const char escape_v[] = "'\"?\\\a\b\f\n\r\t\v\0";
auto c = str[0];
size_t start;
size_t end;
if ((c == '"' || c == '\'') && str[str.size() - 1] == c) {
start = 1;
end = str.size() - 1;
} else {
start = 0;
end = str.size();
}
std::string parsedStr;
char hex[3];
memset(hex, 0, 3);
for (size_t i = start; i < end; i++) {
if (str[i] == '\\' && i < str.size() - 2) {
auto key = strchr(escape_k, str[i + 1]);
if (key) {
parsedStr += escape_v[key - escape_k];
i++;
} else if (str[i + 1] == 'x' && i < str.size() - 4) {
memcpy(hex, str.c_str() + i + 2, 2);
char *p = nullptr;
c = strtol(hex, &p, 16);
if (*p) {
parsedStr += str[i];
} else {
parsedStr += c;
i += 3;
}
} else {
parsedStr += str[i];
}
} else {
parsedStr += str[i];
}
}
return parsedStr;
}
AkVCam::GroupMap AkVCam::SettingsPrivate::groupConfigs()
{
GroupMap groupConfigs;
if (this->m_currentGroup.empty())
groupConfigs = this->m_configs["General"];
else
groupConfigs = this->m_configs[this->m_currentGroup];
return groupConfigs;
}
bool AkVCam::SettingsElement::empty()
{
return this->group.empty()
&& this->key.empty()
&& this->value.empty();
}

59
VCamUtils/src/settings.h Normal file
View file

@ -0,0 +1,59 @@
/* akvirtualcamera, virtual camera for Mac and Windows.
* Copyright (C) 2020 Gonzalo Exequiel Pedone
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef AKVCAM_SETTINGS_H
#define AKVCAM_SETTINGS_H
#include <string>
#include <vector>
namespace AkVCam
{
class SettingsPrivate;
class Fraction;
class Settings
{
public:
Settings();
~Settings();
bool load(const std::string &fileName);
void beginGroup(const std::string &prefix);
void endGroup();
size_t beginArray(const std::string &prefix);
void setArrayIndex(size_t i);
void endArray();
std::vector<std::string> groups() const;
std::vector<std::string> keys() const;
void clear();
bool contains(const std::string &key) const;
std::string value(const std::string &key) const;
bool valueBool(const std::string &key) const;
int32_t valueInt32(const std::string &key) const;
uint32_t valueUInt32(const std::string &key) const;
std::vector<std::string> valueList(const std::string &key,
const std::string &separators) const;
Fraction valueFrac(const std::string &key) const;
private:
SettingsPrivate *d;
};
}
#endif // AKVCAM_SETTINGS_H