diff --git a/domain-server/resources/web/footer.html b/domain-server/resources/web/footer.html index d1a3fc29e8..aa6821a3b9 100644 --- a/domain-server/resources/web/footer.html +++ b/domain-server/resources/web/footer.html @@ -1,3 +1,4 @@ - \ No newline at end of file + + \ No newline at end of file diff --git a/domain-server/resources/web/header.html b/domain-server/resources/web/header.html index 2be603b00e..a2aebc1189 100644 --- a/domain-server/resources/web/header.html +++ b/domain-server/resources/web/header.html @@ -8,4 +8,33 @@ +
\ No newline at end of file diff --git a/domain-server/resources/web/js/domain-server.js b/domain-server/resources/web/js/domain-server.js new file mode 100644 index 0000000000..3f78d8f466 --- /dev/null +++ b/domain-server/resources/web/js/domain-server.js @@ -0,0 +1,10 @@ +$(document).ready(function(){ + var url = window.location; + // Will only work if string in href matches with location + $('ul.nav a[href="'+ url +'"]').parent().addClass('active'); + + // Will also work for relative and absolute hrefs + $('ul.nav a').filter(function() { + return this.href == url; + }).parent().addClass('active'); +}); \ No newline at end of file diff --git a/domain-server/resources/web/settings/describe.json b/domain-server/resources/web/settings/describe.json index 2d90680e01..f4920a7b50 100644 --- a/domain-server/resources/web/settings/describe.json +++ b/domain-server/resources/web/settings/describe.json @@ -28,33 +28,5 @@ "default": "" } } - }, - "voxels": { - "label": "Voxels", - "assignment-types": [2,3], - "settings": { - "voxel-wallet": { - "label": "Destination Wallet ID", - "help": "Wallet to be paid for voxel changes", - "placeholder": "00000000-0000-0000-0000-000000000000", - "default": "" - }, - "per-voxel-credits": { - "type": "double", - "label": "Per Voxel Cost", - "help": "Credit cost to change each voxel", - "placeholder": "0.0", - "default": "0.0", - "input_addon": "₵" - }, - "per-meter-cubed-credits": { - "type": "double", - "label": "Per Meter Cubed Cost", - "help": "Credit cost to change each cubed meter of voxel space", - "placeholder": "0.0", - "default": "0.0", - "input_addon": "₵" - } - } } } \ No newline at end of file diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 2ecae73d0a..a76d3a916d 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1280,6 +1280,9 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl const QByteArray HTTP_COOKIE_HEADER_KEY = "Cookie"; const QString ADMIN_USERS_CONFIG_KEY = "admin-users"; const QString ADMIN_ROLES_CONFIG_KEY = "admin-roles"; + const QString BASIC_AUTH_CONFIG_KEY = "basic-auth"; + + const QByteArray UNAUTHENTICATED_BODY = "You do not have permission to access this domain-server."; if (!_oauthProviderURL.isEmpty() && (_argumentVariantMap.contains(ADMIN_USERS_CONFIG_KEY) || _argumentVariantMap.contains(ADMIN_ROLES_CONFIG_KEY))) { @@ -1293,6 +1296,11 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl cookieUUID = cookieUUIDRegex.cap(1); } + if (_argumentVariantMap.contains(BASIC_AUTH_CONFIG_KEY)) { + qDebug() << "Config file contains web admin settings for OAuth and basic HTTP authentication." + << "These cannot be combined - using OAuth for authentication."; + } + if (!cookieUUID.isNull() && _cookieSessionHash.contains(cookieUUID)) { // pull the QJSONObject for the user with this cookie UUID DomainServerWebSessionData sessionData = _cookieSessionHash.value(cookieUUID); @@ -1315,8 +1323,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl } } - QString unauthenticatedRequest = "You do not have permission to access this domain-server."; - connection->respond(HTTPConnection::StatusCode401, unauthenticatedRequest.toUtf8()); + connection->respond(HTTPConnection::StatusCode401, UNAUTHENTICATED_BODY); // the user does not have allowed username or role, return 401 return false; @@ -1340,6 +1347,59 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl // we don't know about this user yet, so they are not yet authenticated return false; } + } else if (_argumentVariantMap.contains(BASIC_AUTH_CONFIG_KEY)) { + // config file contains username and password combinations for basic auth + const QByteArray BASIC_AUTH_HEADER_KEY = "Authorization"; + + // check if a username and password have been provided with the request + QString basicAuthString = connection->requestHeaders().value(BASIC_AUTH_HEADER_KEY); + + if (!basicAuthString.isEmpty()) { + QStringList splitAuthString = basicAuthString.split(' '); + QString base64String = splitAuthString.size() == 2 ? splitAuthString[1] : ""; + QString credentialString = QByteArray::fromBase64(base64String.toLocal8Bit()); + + if (!credentialString.isEmpty()) { + QStringList credentialList = credentialString.split(':'); + if (credentialList.size() == 2) { + QString username = credentialList[0]; + QString password = credentialList[1]; + + // we've pulled a username and password - now check if there is a match in our basic auth hash + QJsonObject basicAuthObject = _argumentVariantMap.value(BASIC_AUTH_CONFIG_KEY).toJsonValue().toObject(); + + if (basicAuthObject.contains(username)) { + const QString BASIC_AUTH_USER_PASSWORD_KEY = "password"; + QJsonObject userObject = basicAuthObject.value(username).toObject(); + + if (userObject.contains(BASIC_AUTH_USER_PASSWORD_KEY) + && userObject.value(BASIC_AUTH_USER_PASSWORD_KEY).toString() == password) { + // this is username / password match - let this user in + return true; + } + } + } + } + } + + // basic HTTP auth being used but no username and password are present + // or the username and password are not correct + // send back a 401 and ask for basic auth + + const QByteArray HTTP_AUTH_REQUEST_HEADER_KEY = "WWW-Authenticate"; + static QString HTTP_AUTH_REALM_STRING = QString("Basic realm='%1 %2'") + .arg(_hostname.isEmpty() ? "localhost" : _hostname) + .arg("domain-server"); + + Headers basicAuthHeader; + basicAuthHeader.insert(HTTP_AUTH_REQUEST_HEADER_KEY, HTTP_AUTH_REALM_STRING.toUtf8()); + + connection->respond(HTTPConnection::StatusCode401, UNAUTHENTICATED_BODY, + HTTPConnection::DefaultContentType, basicAuthHeader); + + // not authenticated, bubble up false + return false; + } else { // we don't have an OAuth URL + admin roles/usernames, so all users are authenticated return true; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 28640dc092..05a4a08c48 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1502,10 +1502,11 @@ glm::vec3 Application::getMouseVoxelWorldCoordinates(const VoxelDetail& mouseVox } FaceTracker* Application::getActiveFaceTracker() { - return _cara.isActive() ? static_cast(&_cara) : - (_faceshift.isActive() ? static_cast(&_faceshift) : - (_faceplus.isActive() ? static_cast(&_faceplus) : - (_visage.isActive() ? static_cast(&_visage) : NULL))); + return (_dde.isActive() ? static_cast(&_dde) : + (_cara.isActive() ? static_cast(&_cara) : + (_faceshift.isActive() ? static_cast(&_faceshift) : + (_faceplus.isActive() ? static_cast(&_faceplus) : + (_visage.isActive() ? static_cast(&_visage) : NULL))))); } struct SendVoxelsOperationArgs { @@ -1937,13 +1938,21 @@ void Application::updateVisage() { _visage.update(); } +void Application::updateDDE() { + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarnings, "Application::updateDDE()"); + + // Update Cara + _dde.update(); +} + void Application::updateCara() { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateCara()"); - + // Update Cara _cara.update(); - + // Copy angular velocity if measured by cara, to the head if (_cara.isActive()) { _myAvatar->getHead()->setAngularVelocity(_cara.getHeadAngularVelocity()); @@ -3265,6 +3274,7 @@ void Application::resetSensors() { _faceplus.reset(); _faceshift.reset(); _visage.reset(); + _dde.reset(); OculusManager::reset(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 43a6083457..c898d10cbd 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -64,6 +64,7 @@ #include "devices/SixenseManager.h" #include "devices/Visage.h" #include "devices/CaraFaceTracker.h" +#include "devices/DdeFaceTracker.h" #include "models/ModelTreeRenderer.h" #include "particles/ParticleTreeRenderer.h" #include "renderer/AmbientOcclusionEffect.h" @@ -214,6 +215,7 @@ public: Faceplus* getFaceplus() { return &_faceplus; } Faceshift* getFaceshift() { return &_faceshift; } Visage* getVisage() { return &_visage; } + DdeFaceTracker* getDDE() { return &_dde; } CaraFaceTracker* getCara() { return &_cara; } FaceTracker* getActiveFaceTracker(); SixenseManager* getSixenseManager() { return &_sixenseManager; } @@ -393,6 +395,7 @@ private: void updateFaceplus(); void updateFaceshift(); void updateVisage(); + void updateDDE(); void updateCara(); void updateMyAvatarLookAtPosition(); void updateThreads(float deltaTime); @@ -493,6 +496,7 @@ private: Faceshift _faceshift; Visage _visage; CaraFaceTracker _cara; + DdeFaceTracker _dde; SixenseManager _sixenseManager; PrioVR _prioVR; diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index d3d1e74fc8..995f3f2390 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -68,6 +68,9 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { if ((_isFaceshiftConnected = faceTracker)) { _blendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); _isFaceshiftConnected = true; + } else if (Application::getInstance()->getDDE()->isActive()) { + faceTracker = Application::getInstance()->getDDE(); + _blendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); } } diff --git a/interface/src/devices/CaraFaceTracker.h b/interface/src/devices/CaraFaceTracker.h index 41dd205d22..0c715afda9 100644 --- a/interface/src/devices/CaraFaceTracker.h +++ b/interface/src/devices/CaraFaceTracker.h @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hi_fi_CaraFaceTracker_h -#define hi_fi_CaraFaceTracker_h +#ifndef hifi_CaraFaceTracker_h +#define hifi_CaraFaceTracker_h #include @@ -121,4 +121,4 @@ private: int _jawOpenIndex; }; -#endif //endif hi_fi_CaraFaceTracker_h \ No newline at end of file +#endif //endif hifi_CaraFaceTracker_h diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp new file mode 100644 index 0000000000..3dc5a818cc --- /dev/null +++ b/interface/src/devices/DdeFaceTracker.cpp @@ -0,0 +1,245 @@ +// +// DdeFaceTracker.cpp +// +// +// Created by Clement on 8/2/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include +#include +#include +#include + +#include "DdeFaceTracker.h" + +static const QHostAddress DDE_FEATURE_POINT_SERVER_ADDR("127.0.0.1"); +static const quint16 DDE_FEATURE_POINT_SERVER_PORT = 5555; + +static const int NUM_EXPRESSION = 46; +static const int MIN_PACKET_SIZE = (8 + NUM_EXPRESSION) * sizeof(float) + sizeof(int); +static const int MAX_NAME_SIZE = 31; + +struct Packet{ + //roughly in mm + float focal_length[1]; + float translation[3]; + + //quaternion + float rotation[4]; + + //blendshape coefficients ranging between -0.2 and 1.5 + float expressions[NUM_EXPRESSION]; + + //avatar id selected on the UI + int avatar_id; + + //client name, arbitrary length + char name[MAX_NAME_SIZE + 1]; +}; + +DdeFaceTracker::DdeFaceTracker() : +_lastReceiveTimestamp(0), +_reset(false), +_leftBlinkIndex(0), // see http://support.faceshift.com/support/articles/35129-export-of-blendshapes +_rightBlinkIndex(1), +_leftEyeOpenIndex(8), +_rightEyeOpenIndex(9), +_browDownLeftIndex(14), +_browDownRightIndex(15), +_browUpCenterIndex(16), +_browUpLeftIndex(17), +_browUpRightIndex(18), +_mouthSmileLeftIndex(28), +_mouthSmileRightIndex(29), +_jawOpenIndex(21) +{ + _blendshapeCoefficients.resize(NUM_EXPRESSION); + + connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams())); + connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketErrorOccurred(QAbstractSocket::SocketError))); + connect(&_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SLOT(socketStateChanged(QAbstractSocket::SocketState))); + + bindTo(DDE_FEATURE_POINT_SERVER_PORT); +} + +DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 port) : +_lastReceiveTimestamp(0), +_reset(false), +_leftBlinkIndex(0), // see http://support.faceshift.com/support/articles/35129-export-of-blendshapes +_rightBlinkIndex(1), +_leftEyeOpenIndex(8), +_rightEyeOpenIndex(9), +_browDownLeftIndex(14), +_browDownRightIndex(15), +_browUpCenterIndex(16), +_browUpLeftIndex(17), +_browUpRightIndex(18), +_mouthSmileLeftIndex(28), +_mouthSmileRightIndex(29), +_jawOpenIndex(21) +{ + _blendshapeCoefficients.resize(NUM_EXPRESSION); + + connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams())); + connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketErrorOccurred(QAbstractSocket::SocketError))); + connect(&_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SIGNAL(socketStateChanged(QAbstractSocket::SocketState))); + + bindTo(host, port); +} + +DdeFaceTracker::~DdeFaceTracker() { + if(_udpSocket.isOpen()) + _udpSocket.close(); +} + +void DdeFaceTracker::init() { + +} + +void DdeFaceTracker::reset() { + _reset = true; +} + +void DdeFaceTracker::update() { + +} + +void DdeFaceTracker::bindTo(quint16 port) { + bindTo(QHostAddress::Any, port); +} + +void DdeFaceTracker::bindTo(const QHostAddress& host, quint16 port) { + if(_udpSocket.isOpen()) { + _udpSocket.close(); + } + _udpSocket.bind(host, port); +} + +bool DdeFaceTracker::isActive() const { + static const int ACTIVE_TIMEOUT_USECS = 3000000; //3 secs + return (usecTimestampNow() - _lastReceiveTimestamp < ACTIVE_TIMEOUT_USECS); +} + +//private slots and methods +void DdeFaceTracker::socketErrorOccurred(QAbstractSocket::SocketError socketError) { + qDebug() << "[Error] DDE Face Tracker Socket Error: " << _udpSocket.errorString(); +} + +void DdeFaceTracker::socketStateChanged(QAbstractSocket::SocketState socketState) { + QString state; + switch(socketState) { + case QAbstractSocket::BoundState: + state = "Bounded"; + break; + case QAbstractSocket::ClosingState: + state = "Closing"; + break; + case QAbstractSocket::ConnectedState: + state = "Connected"; + break; + case QAbstractSocket::ConnectingState: + state = "Connecting"; + break; + case QAbstractSocket::HostLookupState: + state = "Host Lookup"; + break; + case QAbstractSocket::ListeningState: + state = "Listening"; + break; + case QAbstractSocket::UnconnectedState: + state = "Unconnected"; + break; + } + qDebug() << "[Info] DDE Face Tracker Socket: " << socketState; +} + +void DdeFaceTracker::readPendingDatagrams() { + QByteArray buffer; + while (_udpSocket.hasPendingDatagrams()) { + buffer.resize(_udpSocket.pendingDatagramSize()); + _udpSocket.readDatagram(buffer.data(), buffer.size()); + } + decodePacket(buffer); +} + +float DdeFaceTracker::getBlendshapeCoefficient(int index) const { + return (index >= 0 && index < (int)_blendshapeCoefficients.size()) ? _blendshapeCoefficients[index] : 0.0f; +} + +static const float DDE_MIN_RANGE = -0.2; +static const float DDE_MAX_RANGE = 1.5; +float rescaleCoef(float ddeCoef) { + return (ddeCoef - DDE_MIN_RANGE) / (DDE_MAX_RANGE - DDE_MIN_RANGE); +} + +void DdeFaceTracker::decodePacket(const QByteArray& buffer) { + if(buffer.size() > MIN_PACKET_SIZE) { + Packet packet; + int bytesToCopy = glm::min((int)sizeof(packet), buffer.size()); + memset(&packet.name, '\n', MAX_NAME_SIZE + 1); + memcpy(&packet, buffer.data(), bytesToCopy); + + glm::vec3 translation; + memcpy(&translation, packet.translation, sizeof(packet.translation)); + glm::quat rotation; + memcpy(&rotation, &packet.rotation, sizeof(packet.rotation)); + if (_reset) { + memcpy(&_referenceTranslation, &translation, sizeof(glm::vec3)); + memcpy(&_referenceRotation, &rotation, sizeof(glm::quat)); + _reset = false; + } + + // Compute relative translation + float LEAN_DAMPING_FACTOR = 40; + translation -= _referenceTranslation; + translation /= LEAN_DAMPING_FACTOR; + translation.x *= -1; + + // Compute relative rotation + rotation = glm::inverse(_referenceRotation) * rotation; + + // copy values + _headTranslation = translation; + _headRotation = rotation; + + // Set blendshapes + float BLINK_MAGNIFIER = 2.0f; + _blendshapeCoefficients[_leftBlinkIndex] = rescaleCoef(packet.expressions[1]) * BLINK_MAGNIFIER; + _blendshapeCoefficients[_rightBlinkIndex] = rescaleCoef(packet.expressions[0]) * BLINK_MAGNIFIER; + + float leftBrow = 1.0f - rescaleCoef(packet.expressions[14]); + if (leftBrow < 0.5f) { + _blendshapeCoefficients[_browDownLeftIndex] = 1.0f - 2.0f * leftBrow; + _blendshapeCoefficients[_browUpLeftIndex] = 0.0f; + } else { + _blendshapeCoefficients[_browDownLeftIndex] = 0.0f; + _blendshapeCoefficients[_browUpLeftIndex] = 2.0f * (leftBrow - 0.5f); + } + float rightBrow = 1.0f - rescaleCoef(packet.expressions[15]); + if (rightBrow < 0.5f) { + _blendshapeCoefficients[_browDownRightIndex] = 1.0f - 2.0f * rightBrow; + _blendshapeCoefficients[_browUpRightIndex] = 0.0f; + } else { + _blendshapeCoefficients[_browDownRightIndex] = 0.0f; + _blendshapeCoefficients[_browUpRightIndex] = 2.0f * (rightBrow - 0.5f); + } + + float JAW_OPEN_MAGNIFIER = 1.4f; + _blendshapeCoefficients[_jawOpenIndex] = rescaleCoef(packet.expressions[21]) * JAW_OPEN_MAGNIFIER; + + + _blendshapeCoefficients[_mouthSmileLeftIndex] = rescaleCoef(packet.expressions[24]); + _blendshapeCoefficients[_mouthSmileRightIndex] = rescaleCoef(packet.expressions[23]); + + + } else { + qDebug() << "[Error] DDE Face Tracker Decode Error"; + } + _lastReceiveTimestamp = usecTimestampNow(); +} diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h new file mode 100644 index 0000000000..96707ac94c --- /dev/null +++ b/interface/src/devices/DdeFaceTracker.h @@ -0,0 +1,89 @@ +// +// DdeFaceTracker.h +// +// +// Created by Clement on 8/2/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_DdeFaceTracker_h +#define hifi_DdeFaceTracker_h + +#include + +#include "FaceTracker.h" + +class DdeFaceTracker : public FaceTracker { + Q_OBJECT + +public: + DdeFaceTracker(); + DdeFaceTracker(const QHostAddress& host, quint16 port); + ~DdeFaceTracker(); + + //initialization + void init(); + void reset(); + void update(); + + //sockets + void bindTo(quint16 port); + void bindTo(const QHostAddress& host, quint16 port); + bool isActive() const; + + float getLeftBlink() const { return getBlendshapeCoefficient(_leftBlinkIndex); } + float getRightBlink() const { return getBlendshapeCoefficient(_rightBlinkIndex); } + float getLeftEyeOpen() const { return getBlendshapeCoefficient(_leftEyeOpenIndex); } + float getRightEyeOpen() const { return getBlendshapeCoefficient(_rightEyeOpenIndex); } + + float getBrowDownLeft() const { return getBlendshapeCoefficient(_browDownLeftIndex); } + float getBrowDownRight() const { return getBlendshapeCoefficient(_browDownRightIndex); } + float getBrowUpCenter() const { return getBlendshapeCoefficient(_browUpCenterIndex); } + float getBrowUpLeft() const { return getBlendshapeCoefficient(_browUpLeftIndex); } + float getBrowUpRight() const { return getBlendshapeCoefficient(_browUpRightIndex); } + + float getMouthSize() const { return getBlendshapeCoefficient(_jawOpenIndex); } + float getMouthSmileLeft() const { return getBlendshapeCoefficient(_mouthSmileLeftIndex); } + float getMouthSmileRight() const { return getBlendshapeCoefficient(_mouthSmileRightIndex); } + +private slots: + + //sockets + void socketErrorOccurred(QAbstractSocket::SocketError socketError); + void readPendingDatagrams(); + void socketStateChanged(QAbstractSocket::SocketState socketState); + +private: + float getBlendshapeCoefficient(int index) const; + void decodePacket(const QByteArray& buffer); + + // sockets + QUdpSocket _udpSocket; + quint64 _lastReceiveTimestamp; + + bool _reset; + glm::vec3 _referenceTranslation; + glm::quat _referenceRotation; + + int _leftBlinkIndex; + int _rightBlinkIndex; + int _leftEyeOpenIndex; + int _rightEyeOpenIndex; + + // Brows + int _browDownLeftIndex; + int _browDownRightIndex; + int _browUpCenterIndex; + int _browUpLeftIndex; + int _browUpRightIndex; + + int _mouthSmileLeftIndex; + int _mouthSmileRightIndex; + + int _jawOpenIndex; +}; + +#endif // hifi_DdeFaceTracker_h \ No newline at end of file diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp index 02cce80104..6fe39a8ccf 100644 --- a/libraries/shared/src/HifiConfigVariantMap.cpp +++ b/libraries/shared/src/HifiConfigVariantMap.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include #include @@ -68,29 +69,35 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL int configIndex = argumentList.indexOf(CONFIG_FILE_OPTION); + QString configFilePath; + if (configIndex != -1) { // we have a config file - try and read it - QString configFilePath = argumentList[configIndex + 1]; - QFile configFile(configFilePath); - - if (configFile.exists()) { - qDebug() << "Reading JSON config file at" << configFilePath; - configFile.open(QIODevice::ReadOnly); - - QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll()); - QJsonObject rootObject = configDocument.object(); - - // enumerate the keys of the configDocument object - foreach(const QString& key, rootObject.keys()) { - - if (!mergedMap.contains(key)) { - // no match in existing list, add it - mergedMap.insert(key, QVariant(rootObject[key])); - } + configFilePath = argumentList[configIndex + 1]; + } else { + // no config file - try to read a file at resources/config.json + configFilePath = QCoreApplication::applicationDirPath() + "/resources/config.json"; + } + + QFile configFile(configFilePath); + + if (configFile.exists()) { + qDebug() << "Reading JSON config file at" << configFilePath; + configFile.open(QIODevice::ReadOnly); + + QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll()); + QJsonObject rootObject = configDocument.object(); + + // enumerate the keys of the configDocument object + foreach(const QString& key, rootObject.keys()) { + + if (!mergedMap.contains(key)) { + // no match in existing list, add it + mergedMap.insert(key, QVariant(rootObject[key])); } - } else { - qDebug() << "Could not find JSON config file at" << configFilePath; } + } else { + qDebug() << "Could not find JSON config file at" << configFilePath; } return mergedMap;