mirror of
https://github.com/mii443/rust-openvr.git
synced 2025-08-22 16:25:36 +00:00
1 line
44 KiB
C++
1 line
44 KiB
C++
/************************************************************************************
|
|
|
|
PublicHeader: None
|
|
Filename : OVR_Profile.cpp
|
|
Content : Structs and functions for loading and storing device profile settings
|
|
Created : February 14, 2013
|
|
Notes :
|
|
|
|
Profiles are used to store per-user settings that can be transferred and used
|
|
across multiple applications. For example, player IPD can be configured once
|
|
and reused for a unified experience across games. Configuration and saving of profiles
|
|
can be accomplished in game via the Profile API or by the official Oculus Configuration
|
|
Utility.
|
|
|
|
Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
|
|
|
|
Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
|
|
you may not use the Oculus VR Rift SDK except in compliance with the License,
|
|
which is provided at the time of installation or download, or which
|
|
otherwise accompanies this software in either electronic or hard copy form.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.oculusvr.com/licenses/LICENSE-3.1
|
|
|
|
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
************************************************************************************/
|
|
|
|
#include "OVR_Profile.h"
|
|
#include "OVR_Device.h"
|
|
#include "OVR_JSON.h"
|
|
#include "Kernel/OVR_Types.h"
|
|
#include "Kernel/OVR_SysFile.h"
|
|
#include "Kernel/OVR_Allocator.h"
|
|
#include "Kernel/OVR_Array.h"
|
|
|
|
#ifdef OVR_OS_WIN32
|
|
#include <Shlobj.h>
|
|
#else
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
|
|
#ifdef OVR_OS_LINUX
|
|
#include <unistd.h>
|
|
#include <pwd.h>
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
#define PROFILE_VERSION 2.0
|
|
#define MAX_PROFILE_MAJOR_VERSION 2
|
|
#define MAX_DEVICE_PROFILE_MAJOR_VERSION 1
|
|
|
|
namespace OVR {
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the pathname of the JSON file containing the stored profiles
|
|
String GetBaseOVRPath(bool create_dir)
|
|
{
|
|
String path;
|
|
|
|
#if defined(OVR_OS_WIN32)
|
|
|
|
TCHAR data_path[MAX_PATH];
|
|
SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, NULL, 0, data_path);
|
|
path = String(data_path);
|
|
|
|
path += "/Oculus";
|
|
|
|
if (create_dir)
|
|
{ // Create the Oculus directory if it doesn't exist
|
|
WCHAR wpath[128];
|
|
OVR::UTF8Util::DecodeString(wpath, path.ToCStr());
|
|
|
|
DWORD attrib = GetFileAttributes(wpath);
|
|
bool exists = attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY);
|
|
if (!exists)
|
|
{
|
|
CreateDirectory(wpath, NULL);
|
|
}
|
|
}
|
|
|
|
#elif defined(OVR_OS_MAC)
|
|
|
|
const char* home = getenv("HOME");
|
|
path = home;
|
|
path += "/Library/Preferences/Oculus";
|
|
|
|
if (create_dir)
|
|
{ // Create the Oculus directory if it doesn't exist
|
|
DIR* dir = opendir(path);
|
|
if (dir == NULL)
|
|
{
|
|
mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
|
|
}
|
|
else
|
|
{
|
|
closedir(dir);
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
passwd* pwd = getpwuid(getuid());
|
|
const char* home = pwd->pw_dir;
|
|
path = home;
|
|
path += "/.config/Oculus";
|
|
|
|
if (create_dir)
|
|
{ // Create the Oculus directory if it doesn't exist
|
|
DIR* dir = opendir(path);
|
|
if (dir == NULL)
|
|
{
|
|
mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
|
|
}
|
|
else
|
|
{
|
|
closedir(dir);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
return path;
|
|
}
|
|
|
|
String ProfileManager::GetProfilePath(bool create_dir)
|
|
{
|
|
String path = GetBaseOVRPath(create_dir);
|
|
path += "/ProfileDB.json";
|
|
return path;
|
|
}
|
|
|
|
bool ProfileManager::GetDeviceTags(const DeviceBase* device, String& product, String& serial)
|
|
{
|
|
product = "";
|
|
serial = "";
|
|
|
|
if (device && device->GetType() == Device_HMD)
|
|
{
|
|
HMDDevice* hmd = (HMDDevice*)device;
|
|
|
|
Ptr<SensorDevice> sensor = *(hmd->GetSensor());
|
|
if (sensor)
|
|
{
|
|
SensorInfo sinfo;
|
|
sensor->GetDeviceInfo(&sinfo);
|
|
serial = sinfo.SerialNumber; // get the serial number
|
|
|
|
// Derive the product tag from the HMD product name
|
|
HMDInfo hmdinfo;
|
|
hmd->GetDeviceInfo(&hmdinfo);
|
|
|
|
const char* product_name = NULL;
|
|
|
|
// If the HMD is unrecognized then use the name stamped into the
|
|
// sensor firmware
|
|
if (hmdinfo.HmdType == HmdType_None || hmdinfo.HmdType == HmdType_Unknown)
|
|
product_name = sinfo.ProductName.ToCStr();
|
|
else
|
|
product_name = hmdinfo.ProductName.ToCStr();
|
|
|
|
// First strip off "Oculus"
|
|
const char* oculus = strstr(product_name, "Oculus ");
|
|
if (oculus)
|
|
product_name = oculus + OVR_strlen("Oculus ");
|
|
// And remove spaces from the name
|
|
for (const char* s=product_name; *s != 0; s++)
|
|
{
|
|
if (*s != ' ')
|
|
product.AppendChar(*s);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (!product.IsEmpty() && !serial.IsEmpty());
|
|
}
|
|
|
|
static JSON* FindTaggedData(JSON* data, const char** tag_names, const char** qtags, int num_qtags)
|
|
{
|
|
if (data == NULL || !(data->Name == "TaggedData") || data->Type != JSON_Array)
|
|
return NULL;
|
|
|
|
JSON* tagged_item = data->GetFirstItem();
|
|
while (tagged_item)
|
|
{
|
|
JSON* tags = tagged_item->GetItemByName("tags");
|
|
if (tags->Type == JSON_Array && num_qtags == tags->GetArraySize())
|
|
{ // Check for a full tag match on each item
|
|
int num_matches = 0;
|
|
|
|
for (int k=0; k<num_qtags; k++)
|
|
{
|
|
JSON* tag = tags->GetFirstItem();
|
|
while (tag)
|
|
{
|
|
JSON* tagval = tag->GetFirstItem();
|
|
if (tagval && tagval->Name == tag_names[k])
|
|
{
|
|
if (tagval->Value == qtags[k])
|
|
num_matches++;
|
|
break;
|
|
}
|
|
tag = tags->GetNextItem(tag);
|
|
}
|
|
}
|
|
|
|
// if all tags were matched then copy the values into this Profile
|
|
if (num_matches == num_qtags)
|
|
{
|
|
JSON* vals = tagged_item->GetItemByName("vals");
|
|
return vals;
|
|
}
|
|
}
|
|
|
|
tagged_item = data->GetNextItem(tagged_item);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void FilterTaggedData(JSON* data, const char* tag_name, const char* qtag, Array<JSON*>& items)
|
|
{
|
|
if (data == NULL || !(data->Name == "TaggedData") || data->Type != JSON_Array)
|
|
return;
|
|
|
|
JSON* tagged_item = data->GetFirstItem();
|
|
while (tagged_item)
|
|
{
|
|
JSON* tags = tagged_item->GetItemByName("tags");
|
|
if (tags->Type == JSON_Array)
|
|
{ // Check for a tag match on the requested tag
|
|
|
|
JSON* tag = tags->GetFirstItem();
|
|
while (tag)
|
|
{
|
|
JSON* tagval = tag->GetFirstItem();
|
|
if (tagval && tagval->Name == tag_name)
|
|
{
|
|
if (tagval->Value == qtag)
|
|
{ // Add this item to the output list
|
|
items.PushBack(tagged_item);
|
|
}
|
|
break;
|
|
}
|
|
tag = tags->GetNextItem(tag);
|
|
}
|
|
}
|
|
|
|
tagged_item = data->GetNextItem(tagged_item);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// ***** ProfileManager
|
|
|
|
ProfileManager::ProfileManager()
|
|
{
|
|
Changed = false;
|
|
}
|
|
|
|
ProfileManager::~ProfileManager()
|
|
{
|
|
ClearCache();
|
|
}
|
|
|
|
ProfileManager* ProfileManager::Create()
|
|
{
|
|
return new ProfileManager();
|
|
}
|
|
|
|
// Clear the local profile cache
|
|
void ProfileManager::ClearCache()
|
|
{
|
|
Lock::Locker lockScope(&ProfileLock);
|
|
//ProfileCache.Clear();
|
|
if (ProfileCache)
|
|
{
|
|
//ProfileCache->Release();
|
|
ProfileCache = NULL;
|
|
}
|
|
Changed = false;
|
|
}
|
|
|
|
// Returns a profile with all system default values
|
|
Profile* ProfileManager::GetDefaultProfile(const DeviceBase* device)
|
|
{
|
|
// In the absence of any data, set some reasonable profile defaults.
|
|
// However, this is not future proof and developers should still
|
|
// provide reasonable default values for queried fields.
|
|
Profile* profile = CreateProfile();
|
|
profile->SetValue(OVR_KEY_USER, "default");
|
|
profile->SetValue(OVR_KEY_NAME, "Default");
|
|
profile->SetValue(OVR_KEY_GENDER, OVR_DEFAULT_GENDER);
|
|
profile->SetFloatValue(OVR_KEY_PLAYER_HEIGHT, OVR_DEFAULT_PLAYER_HEIGHT);
|
|
profile->SetFloatValue(OVR_KEY_EYE_HEIGHT, 1.675f);
|
|
profile->SetFloatValue(OVR_KEY_IPD, OVR_DEFAULT_IPD);
|
|
float dist[2] = {OVR_DEFAULT_NECK_TO_EYE_HORIZONTAL, OVR_DEFAULT_NECK_TO_EYE_VERTICAL};
|
|
profile->SetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, dist, 2);
|
|
//profile->SetFloatValue(OVR_KEY_NECK_TO_EYE_VERTICAL, 0.12f);
|
|
|
|
// TODO: Provide device specific defaults
|
|
OVR_UNUSED(device);
|
|
|
|
// DK1 default
|
|
//profile->SetValue("EyeCup", "A");
|
|
|
|
return profile;
|
|
}
|
|
|
|
// Poplulates the local profile cache. This occurs on the first access of the profile
|
|
// data. All profile operations are performed against the local cache until the
|
|
// ProfileManager is released or goes out of scope at which time the cache is serialized
|
|
// to disk.
|
|
void ProfileManager::LoadCache(bool create)
|
|
{
|
|
Lock::Locker lockScope(&ProfileLock);
|
|
|
|
ClearCache();
|
|
|
|
String path = GetProfilePath(false);
|
|
|
|
Ptr<JSON> root = *JSON::Load(path);
|
|
if (root == NULL)
|
|
{
|
|
path = GetBaseOVRPath(false) + "/Profiles.json"; // look for legacy profile
|
|
root = *JSON::Load(path);
|
|
|
|
if (root == NULL)
|
|
{
|
|
if (create)
|
|
{ // Generate a skeleton profile database
|
|
root = *JSON::CreateObject();
|
|
root->AddNumberItem("Oculus Profile Version", 2.0);
|
|
root->AddItem("Users", JSON::CreateArray());
|
|
root->AddItem("TaggedData", JSON::CreateArray());
|
|
ProfileCache = root;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Verify the legacy version
|
|
JSON* version_item = root->GetFirstItem();
|
|
if (version_item->Name == "Oculus Profile Version")
|
|
{
|
|
int major = atoi(version_item->Value.ToCStr());
|
|
if (major != 1)
|
|
return; // don't use the file on unsupported major version number
|
|
}
|
|
else
|
|
{
|
|
return; // invalid file
|
|
}
|
|
|
|
// Convert the legacy format to the new database format
|
|
LoadV1Profiles(root);
|
|
}
|
|
else
|
|
{
|
|
// Verify the file format and version
|
|
JSON* version_item = root->GetFirstItem();
|
|
if (version_item->Name == "Oculus Profile Version")
|
|
{
|
|
int major = atoi(version_item->Value.ToCStr());
|
|
if (major != 2)
|
|
return; // don't use the file on unsupported major version number
|
|
}
|
|
else
|
|
{
|
|
return; // invalid file
|
|
}
|
|
|
|
ProfileCache = root; // store the database contents for traversal
|
|
}
|
|
}
|
|
|
|
void ProfileManager::LoadV1Profiles(JSON* v1)
|
|
{
|
|
JSON* item0 = v1->GetFirstItem();
|
|
JSON* item1 = v1->GetNextItem(item0);
|
|
JSON* item2 = v1->GetNextItem(item1);
|
|
|
|
// Create the new profile database
|
|
Ptr<JSON> root = *JSON::CreateObject();
|
|
root->AddNumberItem("Oculus Profile Version", 2.0);
|
|
root->AddItem("Users", JSON::CreateArray());
|
|
root->AddItem("TaggedData", JSON::CreateArray());
|
|
ProfileCache = root;
|
|
|
|
const char* default_dk1_user = item1->Value;
|
|
|
|
// Read the number of profiles
|
|
int profileCount = (int)item2->dValue;
|
|
JSON* profileItem = item2;
|
|
|
|
for (int p=0; p<profileCount; p++)
|
|
{
|
|
profileItem = root->GetNextItem(profileItem);
|
|
if (profileItem == NULL)
|
|
break;
|
|
|
|
if (profileItem->Name == "Profile")
|
|
{
|
|
// Read the required Name field
|
|
const char* profileName;
|
|
JSON* item = profileItem->GetFirstItem();
|
|
|
|
if (item && (item->Name == "Name"))
|
|
{
|
|
profileName = item->Value;
|
|
}
|
|
else
|
|
{
|
|
return; // invalid field
|
|
}
|
|
|
|
// Read the user profile fields
|
|
if (CreateUser(profileName, profileName))
|
|
{
|
|
const char* tag_names[2] = {"User", "Product"};
|
|
const char* tags[2];
|
|
tags[0] = profileName;
|
|
|
|
Ptr<Profile> user_profile = *CreateProfile();
|
|
user_profile->SetValue(OVR_KEY_NAME, profileName);
|
|
|
|
float neckeye[2] = { 0, 0 };
|
|
|
|
item = profileItem->GetNextItem(item);
|
|
while (item)
|
|
{
|
|
if (item->Type != JSON_Object)
|
|
{
|
|
if (item->Name == OVR_KEY_PLAYER_HEIGHT)
|
|
{ // Add an explicit eye height
|
|
|
|
}
|
|
if (item->Name == "NeckEyeHori")
|
|
neckeye[0] = (float)item->dValue;
|
|
else if (item->Name == "NeckEyeVert")
|
|
neckeye[1] = (float)item->dValue;
|
|
else
|
|
user_profile->SetValue(item);
|
|
}
|
|
else
|
|
{
|
|
// Add the user/device tag values
|
|
const char* device_name = item->Name.ToCStr();
|
|
Ptr<Profile> device_profile = *CreateProfile();
|
|
|
|
JSON* device_item = item->GetFirstItem();
|
|
while (device_item)
|
|
{
|
|
device_profile->SetValue(device_item);
|
|
device_item = item->GetNextItem(device_item);
|
|
}
|
|
|
|
tags[1] = device_name;
|
|
SetTaggedProfile(tag_names, tags, 2, device_profile);
|
|
}
|
|
|
|
item = profileItem->GetNextItem(item);
|
|
}
|
|
|
|
// Add an explicit eye-height field
|
|
float player_height = user_profile->GetFloatValue(OVR_KEY_PLAYER_HEIGHT,
|
|
OVR_DEFAULT_PLAYER_HEIGHT);
|
|
if (player_height > 0)
|
|
{
|
|
char gender[16];
|
|
user_profile->GetValue(OVR_KEY_GENDER, gender, 16);
|
|
|
|
const float EYE_TO_HEADTOP_RATIO = 0.44538f;
|
|
const float MALE_AVG_HEAD_HEIGHT = 0.232f;
|
|
const float FEMALE_AVG_HEAD_HEIGHT = 0.218f;
|
|
|
|
// compute distance from top of skull to the eye
|
|
float head_height;
|
|
if (OVR_strcmp(gender, "Female") == 0)
|
|
head_height = FEMALE_AVG_HEAD_HEIGHT;
|
|
else
|
|
head_height = MALE_AVG_HEAD_HEIGHT;
|
|
|
|
float skull = EYE_TO_HEADTOP_RATIO * head_height;
|
|
float eye_height = player_height - skull;
|
|
|
|
user_profile->SetFloatValue(OVR_KEY_EYE_HEIGHT, eye_height);
|
|
}
|
|
|
|
// Convert NeckEye values to an array
|
|
if (neckeye[0] > 0 && neckeye[1] > 0)
|
|
user_profile->SetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, neckeye, 2);
|
|
|
|
// Add the user tag values
|
|
SetTaggedProfile(tag_names, tags, 1, user_profile);
|
|
}
|
|
}
|
|
}
|
|
|
|
// since V1 profiles were only for DK1, the assign the user to all DK1's
|
|
const char* tag_names[1] = { "Product" };
|
|
const char* tags[1] = { "RiftDK1" };
|
|
Ptr<Profile> product_profile = *CreateProfile();
|
|
product_profile->SetValue("DefaultUser", default_dk1_user);
|
|
SetTaggedProfile(tag_names, tags, 1, product_profile);
|
|
}
|
|
|
|
// Returns the number of stored profiles for this device type
|
|
int ProfileManager::GetUserCount()
|
|
{
|
|
Lock::Locker lockScope(&ProfileLock);
|
|
|
|
if (ProfileCache == NULL)
|
|
{ // Load the cache
|
|
LoadCache(false);
|
|
if (ProfileCache == NULL)
|
|
return 0;
|
|
}
|
|
|
|
JSON* users = ProfileCache->GetItemByName("Users");
|
|
if (users == NULL)
|
|
return 0;
|
|
|
|
return users->GetItemCount();
|
|
}
|
|
|
|
bool ProfileManager::CreateUser(const char* user, const char* name)
|
|
{
|
|
Lock::Locker lockScope(&ProfileLock);
|
|
|
|
if (ProfileCache == NULL)
|
|
{ // Load the cache
|
|
LoadCache(true);
|
|
if (ProfileCache == NULL)
|
|
return false;
|
|
}
|
|
|
|
JSON* users = ProfileCache->GetItemByName("Users");
|
|
if (users == NULL)
|
|
{ // Generate the User section
|
|
users = JSON::CreateArray();
|
|
ProfileCache->AddItem("Users", users);
|
|
//TODO: Insert this before the TaggedData
|
|
}
|
|
|
|
// Search for the pre-existence of this user
|
|
JSON* user_item = users->GetFirstItem();
|
|
int index = 0;
|
|
while (user_item)
|
|
{
|
|
JSON* userid = user_item->GetItemByName("User");
|
|
int compare = OVR_strcmp(user, userid->Value);
|
|
if (compare == 0)
|
|
{ // The user already exists so simply update the fields
|
|
JSON* name_item = user_item->GetItemByName("Name");
|
|
if (name_item && OVR_strcmp(name, name_item->Value) != 0)
|
|
{
|
|
name_item->Value = name;
|
|
Changed = true;
|
|
}
|
|
return true;
|
|
}
|
|
else if (compare < 0)
|
|
{ // A new user should be placed before this item
|
|
break;
|
|
}
|
|
|
|
user_item = users->GetNextItem(user_item);
|
|
index++;
|
|
}
|
|
|
|
// Create and fill the user struct
|
|
JSON* new_user = JSON::CreateObject();
|
|
new_user->AddStringItem(OVR_KEY_USER, user);
|
|
new_user->AddStringItem(OVR_KEY_NAME, name);
|
|
// user_item->AddStringItem("Password", password);
|
|
|
|
if (user_item == NULL)
|
|
users->AddArrayElement(new_user);
|
|
else
|
|
users->InsertArrayElement(index, new_user);
|
|
|
|
Changed = true;
|
|
return true;
|
|
}
|
|
|
|
// Returns the user id of a specific user in the list. The returned
|
|
// memory is locally allocated and should not be stored or deleted. Returns NULL
|
|
// if the index is invalid
|
|
const char* ProfileManager::GetUser(unsigned int index)
|
|
{
|
|
Lock::Locker lockScope(&ProfileLock);
|
|
|
|
if (ProfileCache == NULL)
|
|
{ // Load the cache
|
|
LoadCache(false);
|
|
if (ProfileCache == NULL)
|
|
return NULL;
|
|
}
|
|
|
|
JSON* users = ProfileCache->GetItemByName("Users");
|
|
|
|
if (users && index < users->GetItemCount())
|
|
{
|
|
JSON* user_item = users->GetItemByIndex(index);
|
|
if (user_item)
|
|
{
|
|
JSON* user = user_item->GetFirstItem();
|
|
if (user)
|
|
{
|
|
JSON* userid = user_item->GetItemByName(OVR_KEY_USER);
|
|
if (userid)
|
|
return userid->Value.ToCStr();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool ProfileManager::RemoveUser(const char* user)
|
|
{
|
|
Lock::Locker lockScope(&ProfileLock);
|
|
|
|
if (ProfileCache == NULL)
|
|
{ // Load the cache
|
|
LoadCache(false);
|
|
if (ProfileCache == NULL)
|
|
return true;
|
|
}
|
|
|
|
JSON* users = ProfileCache->GetItemByName("Users");
|
|
if (users == NULL)
|
|
return true;
|
|
|
|
// Remove this user from the User table
|
|
JSON* user_item = users->GetFirstItem();
|
|
while (user_item)
|
|
{
|
|
JSON* userid = user_item->GetItemByName("User");
|
|
if (OVR_strcmp(user, userid->Value) == 0)
|
|
{ // Delete the user entry
|
|
user_item->RemoveNode();
|
|
user_item->Release();
|
|
Changed = true;
|
|
break;
|
|
}
|
|
|
|
user_item = users->GetNextItem(user_item);
|
|
}
|
|
|
|
// Now remove all data entries with this user tag
|
|
JSON* tagged_data = ProfileCache->GetItemByName("TaggedData");
|
|
Array<JSON*> user_items;
|
|
FilterTaggedData(tagged_data, "User", user, user_items);
|
|
for (unsigned int i=0; i<user_items.GetSize(); i++)
|
|
{
|
|
user_items[i]->RemoveNode();
|
|
user_items[i]->Release();
|
|
Changed = true;
|
|
}
|
|
|
|
return Changed;
|
|
}
|
|
|
|
Profile* ProfileManager::CreateProfile()
|
|
{
|
|
Profile* profile = new Profile();
|
|
return profile;
|
|
}
|
|
|
|
// Returns the name of the profile that is marked as the current default user.
|
|
const char* ProfileManager::GetDefaultUser(const DeviceBase* device)
|
|
{
|
|
const char* tag_names[2] = {"Product", "Serial"};
|
|
const char* tags[2];
|
|
|
|
String product;
|
|
String serial;
|
|
if (!GetDeviceTags(device, product, serial))
|
|
return NULL;
|
|
|
|
const char* product_str = product.IsEmpty() ? NULL : product.ToCStr();
|
|
const char* serial_str = serial.IsEmpty() ? NULL : serial.ToCStr();
|
|
|
|
if (product_str && serial_str)
|
|
{
|
|
tags[0] = product_str;
|
|
tags[1] = serial_str;
|
|
// Look for a default user on this specific device
|
|
Ptr<Profile> p = *GetTaggedProfile(tag_names, tags, 2);
|
|
if (p == NULL)
|
|
{ // Look for a default user on this product
|
|
p = *GetTaggedProfile(tag_names, tags, 1);
|
|
}
|
|
|
|
if (p)
|
|
{
|
|
const char* user = p->GetValue("DefaultUser");
|
|
if (user != NULL && user[0] != 0)
|
|
{
|
|
TempBuff = user;
|
|
return TempBuff.ToCStr();
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool ProfileManager::SetDefaultUser(const DeviceBase* device, const char* user)
|
|
{
|
|
const char* tag_names[2] = {"Product", "Serial"};
|
|
const char* tags[2];
|
|
|
|
String product;
|
|
String serial;
|
|
if (!GetDeviceTags(device, product, serial))
|
|
return NULL;
|
|
|
|
const char* product_str = product.IsEmpty() ? NULL : product.ToCStr();
|
|
const char* serial_str = serial.IsEmpty() ? NULL : serial.ToCStr();
|
|
|
|
if (product_str && serial_str)
|
|
{
|
|
tags[0] = product_str;
|
|
tags[1] = serial_str;
|
|
|
|
Ptr<Profile> p = *CreateProfile();
|
|
p->SetValue("DefaultUser", user);
|
|
return SetTaggedProfile(tag_names, tags, 2, p);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
Profile* ProfileManager::GetTaggedProfile(const char** tag_names, const char** tags, int num_tags)
|
|
{
|
|
Lock::Locker lockScope(&ProfileLock);
|
|
|
|
if (ProfileCache == NULL)
|
|
{ // Load the cache
|
|
LoadCache(false);
|
|
if (ProfileCache == NULL)
|
|
return NULL;
|
|
}
|
|
|
|
JSON* tagged_data = ProfileCache->GetItemByName("TaggedData");
|
|
OVR_ASSERT(tagged_data);
|
|
if (tagged_data == NULL)
|
|
return NULL;
|
|
|
|
Profile* profile = new Profile();
|
|
|
|
JSON* vals = FindTaggedData(tagged_data, tag_names, tags, num_tags);
|
|
if (vals)
|
|
{
|
|
JSON* item = vals->GetFirstItem();
|
|
while (item)
|
|
{
|
|
//printf("Add %s, %s\n", item->Name.ToCStr(), item->Value.ToCStr());
|
|
//profile->Settings.Set(item->Name, item->Value);
|
|
profile->SetValue(item);
|
|
item = vals->GetNextItem(item);
|
|
}
|
|
|
|
return profile;
|
|
}
|
|
else
|
|
{
|
|
profile->Release();
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool ProfileManager::SetTaggedProfile(const char** tag_names, const char** tags, int num_tags, Profile* profile)
|
|
{
|
|
Lock::Locker lockScope(&ProfileLock);
|
|
|
|
if (ProfileCache == NULL)
|
|
{ // Load the cache
|
|
LoadCache(true);
|
|
if (ProfileCache == NULL)
|
|
return false; // TODO: Generate a new profile DB
|
|
}
|
|
|
|
JSON* tagged_data = ProfileCache->GetItemByName("TaggedData");
|
|
OVR_ASSERT(tagged_data);
|
|
if (tagged_data == NULL)
|
|
return false;
|
|
|
|
// Get the cached tagged data section
|
|
JSON* vals = FindTaggedData(tagged_data, tag_names, tags, num_tags);
|
|
if (vals == NULL)
|
|
{
|
|
JSON* tagged_item = JSON::CreateObject();
|
|
JSON* taglist = JSON::CreateArray();
|
|
for (int i=0; i<num_tags; i++)
|
|
{
|
|
JSON* k = JSON::CreateObject();
|
|
k->AddStringItem(tag_names[i], tags[i]);
|
|
taglist->AddArrayElement(k);
|
|
}
|
|
|
|
vals = JSON::CreateObject();
|
|
|
|
tagged_item->AddItem("tags", taglist);
|
|
tagged_item->AddItem("vals", vals);
|
|
tagged_data->AddArrayElement(tagged_item);
|
|
}
|
|
|
|
// Now add or update each profile setting in cache
|
|
for (unsigned int i=0; i<profile->Values.GetSize(); i++)
|
|
{
|
|
JSON* value = profile->Values[i];
|
|
|
|
bool found = false;
|
|
JSON* item = vals->GetFirstItem();
|
|
while (item)
|
|
{
|
|
if (value->Name == item->Name)
|
|
{
|
|
// Don't allow a pre-existing type to be overridden
|
|
OVR_ASSERT(value->Type == item->Type);
|
|
|
|
if (value->Type == item->Type)
|
|
{ // Check for the same value
|
|
if (value->Type == JSON_Array)
|
|
{ // Update each array item
|
|
if (item->GetArraySize() == value->GetArraySize())
|
|
{ // Update each value (assumed to be basic types and not array of objects)
|
|
JSON* value_element = value->GetFirstItem();
|
|
JSON* item_element = item->GetFirstItem();
|
|
while (item_element && value_element)
|
|
{
|
|
if (value_element->Type == JSON_String)
|
|
{
|
|
if (item_element->Value != value_element->Value)
|
|
{ // Overwrite the changed value and mark for file update
|
|
item_element->Value = value_element->Value;
|
|
Changed = true;
|
|
}
|
|
}
|
|
else {
|
|
if (item_element->dValue != value_element->dValue)
|
|
{ // Overwrite the changed value and mark for file update
|
|
item_element->dValue = value_element->dValue;
|
|
Changed = true;
|
|
}
|
|
}
|
|
|
|
value_element = value->GetNextItem(value_element);
|
|
item_element = item->GetNextItem(item_element);
|
|
}
|
|
}
|
|
else
|
|
{ // if the array size changed, simply create a new one
|
|
// TODO: Create the new array
|
|
}
|
|
}
|
|
else if (value->Type == JSON_String)
|
|
{
|
|
if (item->Value != value->Value)
|
|
{ // Overwrite the changed value and mark for file update
|
|
item->Value = value->Value;
|
|
Changed = true;
|
|
}
|
|
}
|
|
else {
|
|
if (item->dValue != value->dValue)
|
|
{ // Overwrite the changed value and mark for file update
|
|
item->dValue = value->dValue;
|
|
Changed = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
found = true;
|
|
break;
|
|
}
|
|
|
|
item = vals->GetNextItem(item);
|
|
}
|
|
|
|
if (!found)
|
|
{ // Add the new value
|
|
if (value->Type == JSON_String)
|
|
vals->AddStringItem(value->Name, value->Value);
|
|
else if (value->Type == JSON_Bool)
|
|
vals->AddBoolItem(value->Name, (value->dValue != 0));
|
|
else if (value->Type == JSON_Array)
|
|
vals->AddItem(value->Name, value->Copy());
|
|
else
|
|
vals->AddNumberItem(value->Name, value->dValue);
|
|
|
|
Changed = true;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
Profile* ProfileManager::GetProfile(const DeviceBase* device, const char* user)
|
|
{
|
|
Lock::Locker lockScope(&ProfileLock);
|
|
|
|
if (ProfileCache == NULL)
|
|
{ // Load the cache
|
|
LoadCache(false);
|
|
if (ProfileCache == NULL)
|
|
return NULL;
|
|
}
|
|
|
|
Profile* profile = new Profile();
|
|
|
|
if (device)
|
|
{
|
|
if (!profile->LoadDeviceProfile(device) && (user == NULL))
|
|
{
|
|
profile->Release();
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (user)
|
|
{
|
|
String product;
|
|
String serial;
|
|
GetDeviceTags(device, product, serial);
|
|
|
|
const char* product_str = product.IsEmpty() ? NULL : product.ToCStr();
|
|
const char* serial_str = serial.IsEmpty() ? NULL : serial.ToCStr();
|
|
|
|
if (!profile->LoadProfile(ProfileCache.GetPtr(), user, product_str, serial_str))
|
|
{
|
|
profile->Release();
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return profile;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// ***** Profile
|
|
|
|
Profile::~Profile()
|
|
{
|
|
ValMap.Clear();
|
|
for (unsigned int i=0; i<Values.GetSize(); i++)
|
|
Values[i]->Release();
|
|
|
|
Values.Clear();
|
|
}
|
|
|
|
bool Profile::Close()
|
|
{
|
|
// TODO:
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void Profile::CopyItems(JSON* root, String prefix)
|
|
{
|
|
JSON* item = root->GetFirstItem();
|
|
while (item)
|
|
{
|
|
String item_name;
|
|
if (prefix.IsEmpty())
|
|
item_name = item->Name;
|
|
else
|
|
item_name = prefix + "." + item->Name;
|
|
|
|
if (item->Type == JSON_Object)
|
|
{ // recursively copy the children
|
|
|
|
CopyItems(item, item_name);
|
|
}
|
|
else
|
|
{
|
|
//Settings.Set(item_name, item->Value);
|
|
SetValue(item);
|
|
}
|
|
|
|
item = root->GetNextItem(item);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool Profile::LoadDeviceFile(unsigned int device_id, const char* serial)
|
|
{
|
|
if (serial[0] == 0)
|
|
return false;
|
|
|
|
String path = GetBaseOVRPath(false);
|
|
path += "/Devices.json";
|
|
|
|
// Load the device profiles
|
|
Ptr<JSON> root = *JSON::Load(path);
|
|
if (root == NULL)
|
|
return false;
|
|
|
|
// Quick sanity check of the file type and format before we parse it
|
|
JSON* version = root->GetFirstItem();
|
|
if (version && version->Name == "Oculus Device Profile Version")
|
|
{
|
|
int major = atoi(version->Value.ToCStr());
|
|
if (major > MAX_DEVICE_PROFILE_MAJOR_VERSION)
|
|
return false; // don't parse the file on unsupported major version number
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
JSON* device = root->GetNextItem(version);
|
|
while (device)
|
|
{
|
|
if (device->Name == "Device")
|
|
{
|
|
JSON* product_item = device->GetItemByName("ProductID");
|
|
JSON* serial_item = device->GetItemByName("Serial");
|
|
if (product_item && serial_item
|
|
&& (product_item->dValue == device_id) && (serial_item->Value == serial))
|
|
{
|
|
// found the entry for this device so recursively copy all the settings to the profile
|
|
CopyItems(device, "");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
device = root->GetNextItem(device);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
static int BCDByte(unsigned int byte)
|
|
{
|
|
int digit1 = (byte >> 4) & 0x000f;
|
|
int digit2 = byte & 0x000f;
|
|
int decimal = digit1 * 10 + digit2;
|
|
return decimal;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool Profile::LoadDeviceProfile(const DeviceBase* device)
|
|
{
|
|
bool success = false;
|
|
if (device == NULL)
|
|
return false;
|
|
|
|
SensorDevice* sensor = NULL;
|
|
|
|
if (device->GetType() == Device_HMD)
|
|
{
|
|
// Convert the HMD device to Sensor
|
|
sensor = ((HMDDevice*)device)->GetSensor();
|
|
device = sensor;
|
|
if (device == NULL)
|
|
return false;
|
|
}
|
|
|
|
if (device->GetType() == Device_Sensor)
|
|
{
|
|
SensorDevice* sensor = (SensorDevice*)device;
|
|
|
|
SensorInfo sinfo;
|
|
sensor->GetDeviceInfo(&sinfo);
|
|
|
|
int dev_major = BCDByte((sinfo.Version >> 8) & 0x00ff);
|
|
OVR_UNUSED(dev_major);
|
|
int dev_minor = BCDByte(sinfo.Version & 0xff);
|
|
|
|
if (dev_minor > 18)
|
|
{ // If the firmware supports hardware stored profiles then grab the device profile
|
|
// from the sensor
|
|
// TBD: Implement this
|
|
}
|
|
else
|
|
{
|
|
// Grab the model and serial number from the device and use it to access the device
|
|
// profile file stored on the local machine
|
|
success = LoadDeviceFile(sinfo.ProductId, sinfo.SerialNumber);
|
|
}
|
|
}
|
|
|
|
if (sensor)
|
|
sensor->Release(); // release the sensor handle
|
|
|
|
return success;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool Profile::LoadUser(JSON* root,
|
|
const char* user,
|
|
const char* model_name,
|
|
const char* device_serial)
|
|
{
|
|
if (user == NULL)
|
|
return false;
|
|
|
|
// For legacy files, convert to old style names
|
|
//if (model_name && OVR_strcmp(model_name, "Oculus Rift DK1") == 0)
|
|
// model_name = "RiftDK1";
|
|
|
|
bool user_found = false;
|
|
JSON* data = root->GetItemByName("TaggedData");
|
|
if (data)
|
|
{
|
|
const char* tag_names[3];
|
|
const char* tags[3];
|
|
tag_names[0] = "User";
|
|
tags[0] = user;
|
|
int num_tags = 1;
|
|
|
|
if (model_name)
|
|
{
|
|
tag_names[num_tags] = "Product";
|
|
tags[num_tags] = model_name;
|
|
num_tags++;
|
|
}
|
|
|
|
if (device_serial)
|
|
{
|
|
tag_names[num_tags] = "Serial";
|
|
tags[num_tags] = device_serial;
|
|
num_tags++;
|
|
}
|
|
|
|
// Retrieve all tag permutations
|
|
for (int combos=1; combos<=num_tags; combos++)
|
|
{
|
|
for (int i=0; i<(num_tags - combos + 1); i++)
|
|
{
|
|
JSON* vals = FindTaggedData(data, tag_names+i, tags+i, combos);
|
|
if (vals)
|
|
{
|
|
if (i==0) // This tag-combination contains a user match
|
|
user_found = true;
|
|
|
|
// Add the values to the Profile. More specialized multi-tag values
|
|
// will take precedence over and overwrite generalized ones
|
|
// For example: ("Me","RiftDK1").IPD would overwrite ("Me").IPD
|
|
JSON* item = vals->GetFirstItem();
|
|
while (item)
|
|
{
|
|
//printf("Add %s, %s\n", item->Name.ToCStr(), item->Value.ToCStr());
|
|
//Settings.Set(item->Name, item->Value);
|
|
SetValue(item);
|
|
item = vals->GetNextItem(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (user_found)
|
|
SetValue(OVR_KEY_USER, user);
|
|
|
|
return user_found;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool Profile::LoadProfile(JSON* root,
|
|
const char* user,
|
|
const char* device_model,
|
|
const char* device_serial)
|
|
{
|
|
if (!LoadUser(root, user, device_model, device_serial))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
char* Profile::GetValue(const char* key, char* val, int val_length) const
|
|
{
|
|
JSON* value = NULL;
|
|
if (ValMap.Get(key, &value))
|
|
{
|
|
OVR_strcpy(val, val_length, value->Value.ToCStr());
|
|
return val;
|
|
}
|
|
else
|
|
{
|
|
val[0] = 0;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
const char* Profile::GetValue(const char* key)
|
|
{
|
|
// Non-reentrant query. The returned buffer can only be used until the next call
|
|
// to GetValue()
|
|
JSON* value = NULL;
|
|
if (ValMap.Get(key, &value))
|
|
{
|
|
TempVal = value->Value;
|
|
return TempVal.ToCStr();
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
int Profile::GetNumValues(const char* key) const
|
|
{
|
|
JSON* value = NULL;
|
|
if (ValMap.Get(key, &value))
|
|
{
|
|
if (value->Type == JSON_Array)
|
|
return value->GetArraySize();
|
|
else
|
|
return 1;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool Profile::GetBoolValue(const char* key, bool default_val) const
|
|
{
|
|
JSON* value = NULL;
|
|
if (ValMap.Get(key, &value) && value->Type == JSON_Bool)
|
|
return (value->dValue != 0);
|
|
else
|
|
return default_val;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
int Profile::GetIntValue(const char* key, int default_val) const
|
|
{
|
|
JSON* value = NULL;
|
|
if (ValMap.Get(key, &value) && value->Type == JSON_Number)
|
|
return (int)(value->dValue);
|
|
else
|
|
return default_val;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
float Profile::GetFloatValue(const char* key, float default_val) const
|
|
{
|
|
JSON* value = NULL;
|
|
if (ValMap.Get(key, &value) && value->Type == JSON_Number)
|
|
return (float)(value->dValue);
|
|
else
|
|
return default_val;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
int Profile::GetFloatValues(const char* key, float* values, int num_vals) const
|
|
{
|
|
JSON* value = NULL;
|
|
if (ValMap.Get(key, &value) && value->Type == JSON_Array)
|
|
{
|
|
int val_count = Alg::Min(value->GetArraySize(), num_vals);
|
|
JSON* item = value->GetFirstItem();
|
|
int count=0;
|
|
while (item && count < val_count)
|
|
{
|
|
if (item->Type == JSON_Number)
|
|
values[count] = (float)item->dValue;
|
|
else
|
|
break;
|
|
|
|
count++;
|
|
item = value->GetNextItem(item);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
double Profile::GetDoubleValue(const char* key, double default_val) const
|
|
{
|
|
JSON* value = NULL;
|
|
if (ValMap.Get(key, &value) && value->Type == JSON_Number)
|
|
return value->dValue;
|
|
else
|
|
return default_val;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
int Profile::GetDoubleValues(const char* key, double* values, int num_vals) const
|
|
{
|
|
JSON* value = NULL;
|
|
if (ValMap.Get(key, &value) && value->Type == JSON_Array)
|
|
{
|
|
int val_count = Alg::Min(value->GetArraySize(), num_vals);
|
|
JSON* item = value->GetFirstItem();
|
|
int count=0;
|
|
while (item && count < val_count)
|
|
{
|
|
if (item->Type == JSON_Number)
|
|
values[count] = item->dValue;
|
|
else
|
|
break;
|
|
|
|
count++;
|
|
item = value->GetNextItem(item);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void Profile::SetValue(JSON* val)
|
|
{
|
|
if (val->Type == JSON_Number)
|
|
SetDoubleValue(val->Name, val->dValue);
|
|
else if (val->Type == JSON_Bool)
|
|
SetBoolValue(val->Name, (val->dValue != 0));
|
|
else if (val->Type == JSON_String)
|
|
SetValue(val->Name, val->Value);
|
|
else if (val->Type == JSON_Array)
|
|
{
|
|
if (val == NULL)
|
|
return;
|
|
|
|
// Create a copy of the array
|
|
JSON* value = val->Copy();
|
|
Values.PushBack(value);
|
|
ValMap.Set(value->Name, value);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void Profile::SetValue(const char* key, const char* val)
|
|
{
|
|
if (key == NULL || val == NULL)
|
|
return;
|
|
|
|
JSON* value = NULL;
|
|
if (ValMap.Get(key, &value))
|
|
{
|
|
value->Value = val;
|
|
}
|
|
else
|
|
{
|
|
value = JSON::CreateString(val);
|
|
value->Name = key;
|
|
|
|
Values.PushBack(value);
|
|
ValMap.Set(key, value);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void Profile::SetBoolValue(const char* key, bool val)
|
|
{
|
|
if (key == NULL)
|
|
return;
|
|
|
|
JSON* value = NULL;
|
|
if (ValMap.Get(key, &value))
|
|
{
|
|
value->dValue = val;
|
|
}
|
|
else
|
|
{
|
|
value = JSON::CreateBool(val);
|
|
value->Name = key;
|
|
|
|
Values.PushBack(value);
|
|
ValMap.Set(key, value);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void Profile::SetIntValue(const char* key, int val)
|
|
{
|
|
SetDoubleValue(key, val);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void Profile::SetFloatValue(const char* key, float val)
|
|
{
|
|
SetDoubleValue(key, val);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void Profile::SetFloatValues(const char* key, const float* vals, int num_vals)
|
|
{
|
|
JSON* value = NULL;
|
|
int val_count = 0;
|
|
if (ValMap.Get(key, &value))
|
|
{
|
|
if (value->Type == JSON_Array)
|
|
{
|
|
// truncate the existing array if fewer entries provided
|
|
int num_existing_vals = value->GetArraySize();
|
|
for (int i=num_vals; i<num_existing_vals; i++)
|
|
value->RemoveLast();
|
|
|
|
JSON* item = value->GetFirstItem();
|
|
while (item && val_count < num_vals)
|
|
{
|
|
if (item->Type == JSON_Number)
|
|
item->dValue = vals[val_count];
|
|
|
|
item = value->GetNextItem(item);
|
|
val_count++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return; // Maybe we should change the data type?
|
|
}
|
|
}
|
|
else
|
|
{
|
|
value = JSON::CreateArray();
|
|
value->Name = key;
|
|
|
|
Values.PushBack(value);
|
|
ValMap.Set(key, value);
|
|
}
|
|
|
|
for (; val_count < num_vals; val_count++)
|
|
value->AddArrayNumber(vals[val_count]);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void Profile::SetDoubleValue(const char* key, double val)
|
|
{
|
|
JSON* value = NULL;
|
|
if (ValMap.Get(key, &value))
|
|
{
|
|
value->dValue = val;
|
|
}
|
|
else
|
|
{
|
|
value = JSON::CreateNumber(val);
|
|
value->Name = key;
|
|
|
|
Values.PushBack(value);
|
|
ValMap.Set(key, value);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void Profile::SetDoubleValues(const char* key, const double* vals, int num_vals)
|
|
{
|
|
JSON* value = NULL;
|
|
int val_count = 0;
|
|
if (ValMap.Get(key, &value))
|
|
{
|
|
if (value->Type == JSON_Array)
|
|
{
|
|
// truncate the existing array if fewer entries provided
|
|
int num_existing_vals = value->GetArraySize();
|
|
for (int i=num_vals; i<num_existing_vals; i++)
|
|
value->RemoveLast();
|
|
|
|
JSON* item = value->GetFirstItem();
|
|
while (item && val_count < num_vals)
|
|
{
|
|
if (item->Type == JSON_Number)
|
|
item->dValue = vals[val_count];
|
|
|
|
item = value->GetNextItem(item);
|
|
val_count++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return; // Maybe we should change the data type?
|
|
}
|
|
}
|
|
else
|
|
{
|
|
value = JSON::CreateArray();
|
|
value->Name = key;
|
|
|
|
Values.PushBack(value);
|
|
ValMap.Set(key, value);
|
|
}
|
|
|
|
for (; val_count < num_vals; val_count++)
|
|
value->AddArrayNumber(vals[val_count]);
|
|
}
|
|
|
|
} // OVR
|