diff options
author | Brad Davis <[email protected]> | 2014-04-14 21:25:09 -0700 |
---|---|---|
committer | Brad Davis <[email protected]> | 2014-04-14 21:25:09 -0700 |
commit | 07d0f4d0bbf3477ac6a9584f726e8ec6ab285707 (patch) | |
tree | 1854d0c690eff32e77b137567c88a52d56d8b660 /LibOVR/Src/OVR_Profile.cpp | |
parent | f28388ff2af14b56ef2d973b2f4f9da021716d4c (diff) |
Adding windows 0.3.1 SDK
Diffstat (limited to 'LibOVR/Src/OVR_Profile.cpp')
-rw-r--r-- | LibOVR/Src/OVR_Profile.cpp | 1572 |
1 files changed, 1126 insertions, 446 deletions
diff --git a/LibOVR/Src/OVR_Profile.cpp b/LibOVR/Src/OVR_Profile.cpp index fdac5d7..1fa8eb4 100644 --- a/LibOVR/Src/OVR_Profile.cpp +++ b/LibOVR/Src/OVR_Profile.cpp @@ -12,16 +12,16 @@ Notes : can be accomplished in game via the Profile API or by the official Oculus Configuration Utility. -Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. -Licensed under the Oculus VR SDK License Version 2.0 (the "License"); -you may not use the Oculus VR SDK except in compliance with the License, +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-2.0 +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, @@ -32,6 +32,7 @@ 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" @@ -51,8 +52,10 @@ limitations under the License. #endif -#define PROFILE_VERSION 1.0 -#define MAX_PROFILE_MAJOR_VERSION 1 + +#define PROFILE_VERSION 2.0 +#define MAX_PROFILE_MAJOR_VERSION 2 +#define MAX_DEVICE_PROFILE_MAJOR_VERSION 1 namespace OVR { @@ -127,28 +130,143 @@ String GetBaseOVRPath(bool create_dir) return path; } -String GetProfilePath(bool create_dir) +String ProfileManager::GetProfilePath(bool create_dir) { String path = GetBaseOVRPath(create_dir); - path += "/Profiles.json"; + 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.Type == 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; - CacheDevice = Profile_Unknown; } ProfileManager::~ProfileManager() { - // If the profiles have been altered then write out the profile file - if (Changed) - SaveCache(); - ClearCache(); } @@ -157,49 +275,50 @@ ProfileManager* ProfileManager::Create() return new ProfileManager(); } -Profile* ProfileManager::CreateProfileObject(const char* user, - ProfileType device, - const char** device_name) +// Clear the local profile cache +void ProfileManager::ClearCache() { Lock::Locker lockScope(&ProfileLock); - - Profile* profile = NULL; - switch (device) + //ProfileCache.Clear(); + if (ProfileCache) { - case Profile_GenericHMD: - *device_name = NULL; - profile = new HMDProfile(Profile_GenericHMD, user); - break; - case Profile_RiftDK1: - *device_name = "RiftDK1"; - profile = new RiftDK1Profile(user); - break; - case Profile_RiftDKHD: - *device_name = "RiftDKHD"; - profile = new RiftDKHDProfile(user); - break; - case Profile_Unknown: - break; + //ProfileCache->Release(); + ProfileCache = NULL; } - - return profile; + Changed = false; } - -// Clear the local profile cache -void ProfileManager::ClearCache() +// Returns a profile with all system default values +Profile* ProfileManager::GetDefaultProfile(const DeviceBase* device) { - Lock::Locker lockScope(&ProfileLock); + // 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"); - ProfileCache.Clear(); - CacheDevice = Profile_Unknown; + 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(ProfileType device) +void ProfileManager::LoadCache(bool create) { Lock::Locker lockScope(&ProfileLock); @@ -208,27 +327,75 @@ void ProfileManager::LoadCache(ProfileType device) String path = GetProfilePath(false); Ptr<JSON> root = *JSON::Load(path); - if (!root || root->GetItemCount() < 3) - return; + 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; + } - // First read the file type and version to make sure this is a valid file - JSON* item0 = root->GetFirstItem(); - JSON* item1 = root->GetNextItem(item0); - JSON* item2 = root->GetNextItem(item1); + // 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 + } - if (item0->Name == "Oculus Profile Version") - { - int major = atoi(item0->Value.ToCStr()); - if (major > MAX_PROFILE_MAJOR_VERSION) - return; // don't parse the file on unsupported major version number + // Convert the legacy format to the new database format + LoadV1Profiles(root); } else { - return; + // 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); - DefaultProfile = item1->Value; + // 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; @@ -253,585 +420,1098 @@ void ProfileManager::LoadCache(ProfileType device) { 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; - const char* deviceName = 0; - bool deviceFound = false; - Ptr<Profile> profile = *CreateProfileObject(profileName, device, &deviceName); + Ptr<Profile> user_profile = *CreateProfile(); + user_profile->SetValue(OVR_KEY_NAME, profileName); - // Read the base profile fields. - if (profile) - { - while (item = profileItem->GetNextItem(item), item) + float neckeye[2] = { 0, 0 }; + + item = profileItem->GetNextItem(item); + while (item) { if (item->Type != JSON_Object) { - profile->ParseProperty(item->Name, item->Value); + 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 - { // Search for the matching device to get device specific fields - if (!deviceFound && deviceName && OVR_strcmp(item->Name, deviceName) == 0) - { - deviceFound = true; + { + // Add the user/device tag values + const char* device_name = item->Name.ToCStr(); + Ptr<Profile> device_profile = *CreateProfile(); - for (JSON* deviceItem = item->GetFirstItem(); deviceItem; - deviceItem = item->GetNextItem(deviceItem)) - { - profile->ParseProperty(deviceItem->Name, deviceItem->Value); - } + 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 the new profile - ProfileCache.PushBack(profile); + // 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); + } } } - CacheDevice = device; + // 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(); +} -// Serializes the profiles to disk. -void ProfileManager::SaveCache() +bool ProfileManager::CreateUser(const char* user, const char* name) { - String path = GetProfilePath(true); - Lock::Locker lockScope(&ProfileLock); - Ptr<JSON> oldroot = *JSON::Load(path); - if (oldroot) - { - if (oldroot->GetItemCount() >= 3) - { - JSON* item0 = oldroot->GetFirstItem(); - JSON* item1 = oldroot->GetNextItem(item0); - oldroot->GetNextItem(item1); + if (ProfileCache == NULL) + { // Load the cache + LoadCache(true); + if (ProfileCache == NULL) + return false; + } - if (item0->Name == "Oculus Profile Version") - { - int major = atoi(item0->Value.ToCStr()); - if (major > MAX_PROFILE_MAJOR_VERSION) - oldroot.Clear(); // don't use the file on unsupported major version number - } - else + 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) { - oldroot.Clear(); + name_item->Value = name; + Changed = true; } + return true; } - else - { - oldroot.Clear(); + else if (compare < 0) + { // A new user should be placed before this item + break; } + + user_item = users->GetNextItem(user_item); + index++; } - - // Create a new json root - Ptr<JSON> root = *JSON::CreateObject(); - root->AddNumberItem("Oculus Profile Version", PROFILE_VERSION); - root->AddStringItem("CurrentProfile", DefaultProfile); - root->AddNumberItem("ProfileCount", (double) ProfileCache.GetSize()); - // Generate a JSON subtree for each profile - for (unsigned int i=0; i<ProfileCache.GetSize(); i++) - { - Profile* profile = ProfileCache[i]; + // 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); - // Write the base profile information - JSON* json_profile = JSON::CreateObject(); - json_profile->Name = "Profile"; - json_profile->AddStringItem("Name", profile->Name); - const char* gender; - switch (profile->GetGender()) - { - case Profile::Gender_Male: gender = "Male"; break; - case Profile::Gender_Female: gender = "Female"; break; - default: gender = "Unspecified"; - } - json_profile->AddStringItem("Gender", gender); - json_profile->AddNumberItem("PlayerHeight", profile->PlayerHeight); - json_profile->AddNumberItem("IPD", profile->IPD); + if (user_item == NULL) + users->AddArrayElement(new_user); + else + users->InsertArrayElement(index, new_user); - const char* device_name = NULL; - // Create a device-specific subtree for the cached device - if (profile->Type == Profile_RiftDK1) - { - device_name = "RiftDK1"; - - RiftDK1Profile* rift = (RiftDK1Profile*)profile; - JSON* json_rift = JSON::CreateObject(); - json_profile->AddItem(device_name, json_rift); + Changed = true; + return true; +} - const char* eyecup = "A"; - switch (rift->EyeCups) - { - case EyeCup_A: eyecup = "A"; break; - case EyeCup_B: eyecup = "B"; break; - case EyeCup_C: eyecup = "C"; break; - } - json_rift->AddStringItem("EyeCup", eyecup); - json_rift->AddNumberItem("LL", rift->LL); - json_rift->AddNumberItem("LR", rift->LR); - json_rift->AddNumberItem("RL", rift->RL); - json_rift->AddNumberItem("RR", rift->RR); - } - else if (profile->Type == Profile_RiftDKHD) - { - device_name = "RiftDKHD"; - - RiftDKHDProfile* rift = (RiftDKHDProfile*)profile; - JSON* json_rift = JSON::CreateObject(); - json_profile->AddItem(device_name, json_rift); +// 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); - const char* eyecup = "A"; - switch (rift->EyeCups) - { - case EyeCup_A: eyecup = "A"; break; - case EyeCup_B: eyecup = "B"; break; - case EyeCup_C: eyecup = "C"; break; - } - json_rift->AddStringItem("EyeCup", eyecup); - //json_rift->AddNumberItem("LL", rift->LL); - //json_rift->AddNumberItem("LR", rift->LR); - //json_rift->AddNumberItem("RL", rift->RL); - //json_rift->AddNumberItem("RR", rift->RR); - } - - // There may be multiple devices stored per user, but only a single - // device is represented by this root. We don't want to overwrite - // the other devices so we need to examine the older root - // and merge previous devices into new json root - if (oldroot) + 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* old_profile = oldroot->GetFirstItem(); - while (old_profile) + JSON* user = user_item->GetFirstItem(); + if (user) { - if (old_profile->Name == "Profile") - { - JSON* profile_name = old_profile->GetItemByName("Name"); - if (profile_name && OVR_strcmp(profile->Name, profile_name->Value) == 0) - { // Now that we found the user in the older root, add all the - // object children to the new root - except for the one for the - // current device - JSON* old_item = old_profile->GetFirstItem(); - while (old_item) - { - if (old_item->Type == JSON_Object - && (device_name == NULL || OVR_strcmp(old_item->Name, device_name) != 0)) - { - JSON* old_device = old_item; - old_item = old_profile->GetNextItem(old_item); - - // remove the node from the older root to avoid multiple reference - old_device->RemoveNode(); - // add the node pointer to the new root - json_profile->AddItem(old_device->Name, old_device); - } - else - { - old_item = old_profile->GetNextItem(old_item); - } - } - - break; - } - } - - old_profile = oldroot->GetNextItem(old_profile); + JSON* userid = user_item->GetItemByName(OVR_KEY_USER); + if (userid) + return userid->Value.ToCStr(); } } - - // Add the completed user profile to the new root - root->AddItem("Profile", json_profile); } + - // Save the profile to disk - root->Save(path); + return NULL; } -// Returns the number of stored profiles for this device type -int ProfileManager::GetProfileCount(ProfileType device) +bool ProfileManager::RemoveUser(const char* user) { Lock::Locker lockScope(&ProfileLock); - if (CacheDevice == Profile_Unknown) - LoadCache(device); + 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; +} - return (int)ProfileCache.GetSize(); +Profile* ProfileManager::CreateProfile() +{ + Profile* profile = new Profile(); + return profile; } -// Returns the profile name of a specific profile 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::GetProfileName(ProfileType device, unsigned int index) +// Returns the name of the profile that is marked as the current default user. +const char* ProfileManager::GetDefaultUser(const DeviceBase* device) { - Lock::Locker lockScope(&ProfileLock); + const char* tag_names[2] = {"Product", "Serial"}; + const char* tags[2]; - if (CacheDevice == Profile_Unknown) - LoadCache(device); + String product; + String serial; + if (!GetDeviceTags(device, product, serial)) + return NULL; - if (index < ProfileCache.GetSize()) - { - Profile* profile = ProfileCache[index]; - OVR_strcpy(NameBuff, Profile::MaxNameLen, profile->Name); - return NameBuff; - } - else + const char* product_str = product.IsEmpty() ? NULL : product.ToCStr(); + const char* serial_str = serial.IsEmpty() ? NULL : serial.ToCStr(); + + if (product_str && serial_str) { - return NULL; + 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::HasProfile(ProfileType device, const char* name) +//----------------------------------------------------------------------------- +bool ProfileManager::SetDefaultUser(const DeviceBase* device, const char* user) { - Lock::Locker lockScope(&ProfileLock); + const char* tag_names[2] = {"Product", "Serial"}; + const char* tags[2]; + + String product; + String serial; + if (!GetDeviceTags(device, product, serial)) + return NULL; - if (CacheDevice == Profile_Unknown) - LoadCache(device); + const char* product_str = product.IsEmpty() ? NULL : product.ToCStr(); + const char* serial_str = serial.IsEmpty() ? NULL : serial.ToCStr(); - for (unsigned i = 0; i< ProfileCache.GetSize(); i++) + if (product_str && serial_str) { - if (ProfileCache[i] && OVR_strcmp(ProfileCache[i]->Name, name) == 0) - return true; + 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; } - -// Returns a specific profile object in the list. The returned memory should be -// encapsulated in a Ptr<> object or released after use. Returns NULL if the index -// is invalid -Profile* ProfileManager::LoadProfile(ProfileType device, unsigned int index) +//----------------------------------------------------------------------------- +Profile* ProfileManager::GetTaggedProfile(const char** tag_names, const char** tags, int num_tags) { Lock::Locker lockScope(&ProfileLock); - if (CacheDevice == Profile_Unknown) - LoadCache(device); + if (ProfileCache == NULL) + { // Load the cache + LoadCache(false); + if (ProfileCache == NULL) + return NULL; + } - if (index < ProfileCache.GetSize()) - { - Profile* profile = ProfileCache[index]; - return profile->Clone(); + JSON* tagged_data = ProfileCache->GetItemByName("TaggedData"); + OVR_ASSERT(tagged_data); + if (tagged_data == NULL) + return false; + + 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; } } -// Returns a profile object for a particular device and user name. The returned -// memory should be encapsulated in a Ptr<> object or released after use. Returns -// NULL if the profile is not found -Profile* ProfileManager::LoadProfile(ProfileType device, const char* user) +//----------------------------------------------------------------------------- +bool ProfileManager::SetTaggedProfile(const char** tag_names, const char** tags, int num_tags, Profile* profile) { - if (user == NULL) - return NULL; - Lock::Locker lockScope(&ProfileLock); - - if (CacheDevice == Profile_Unknown) - LoadCache(device); - for (unsigned int i=0; i<ProfileCache.GetSize(); i++) - { - if (OVR_strcmp(user, ProfileCache[i]->Name) == 0) - { // Found the requested user profile - Profile* profile = ProfileCache[i]; - return profile->Clone(); + 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); } - return NULL; -} + // 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; + } -// Returns a profile with all system default values -Profile* ProfileManager::GetDeviceDefaultProfile(ProfileType device) -{ - const char* device_name = NULL; - return CreateProfileObject("default", device, &device_name); + 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; } -// Returns the name of the profile that is marked as the current default user. -const char* ProfileManager::GetDefaultProfileName(ProfileType device) +//----------------------------------------------------------------------------- +Profile* ProfileManager::GetProfile(const DeviceBase* device, const char* user) { Lock::Locker lockScope(&ProfileLock); - if (CacheDevice == Profile_Unknown) - LoadCache(device); + if (ProfileCache == NULL) + { // Load the cache + LoadCache(false); + if (ProfileCache == NULL) + return NULL; + } + + Profile* profile = new Profile(); - if (ProfileCache.GetSize() > 0) + if (device) { - OVR_strcpy(NameBuff, Profile::MaxNameLen, DefaultProfile); - return NameBuff; + if (!profile->LoadDeviceProfile(device) && (user == NULL)) + { + profile->Release(); + return NULL; + } } - else + + if (user) { - return NULL; + 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(); } -// Marks a particular user as the current default user. -bool ProfileManager::SetDefaultProfileName(ProfileType device, const char* name) +bool Profile::Close() { - Lock::Locker lockScope(&ProfileLock); + // TODO: + return true; +} - if (CacheDevice == Profile_Unknown) - LoadCache(device); -// TODO: I should verify that the user is valid - if (ProfileCache.GetSize() > 0) +//----------------------------------------------------------------------------- +void Profile::CopyItems(JSON* root, String prefix) +{ + JSON* item = root->GetFirstItem(); + while (item) { - DefaultProfile = name; - Changed = true; - return true; + 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; +} -// Saves a new or existing profile. Returns true on success or false on an -// invalid or failed save. -bool ProfileManager::Save(const Profile* profile) +//----------------------------------------------------------------------------- +bool Profile::LoadDeviceProfile(const DeviceBase* device) { - Lock::Locker lockScope(&ProfileLock); + bool success = false; + if (device == NULL) + return false; - if (OVR_strcmp(profile->Name, "default") == 0) - return false; // don't save a default profile + SensorDevice* sensor = NULL; - // TODO: I should also verify that this profile type matches the current cache - if (CacheDevice == Profile_Unknown) - LoadCache(profile->Type); + if (device->GetType() == Device_HMD) + { + // Convert the HMD device to Sensor + sensor = ((HMDDevice*)device)->GetSensor(); + device = sensor; + if (device == NULL) + return false; + } - // Look for the pre-existence of this profile - bool added = false; - for (unsigned int i=0; i<ProfileCache.GetSize(); i++) + if (device->GetType() == Device_Sensor) { - int compare = OVR_strcmp(profile->Name, ProfileCache[i]->Name); - - if (compare == 0) - { - // TODO: I should do a proper field comparison to avoid unnecessary - // overwrites and file saves - - // Replace the previous instance with the new profile - ProfileCache[i] = *profile->Clone(); - added = true; - Changed = true; - break; + 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 (!added) - { - ProfileCache.PushBack(*profile->Clone()); - if (ProfileCache.GetSize() == 1) - CacheDevice = profile->Type; - - Changed = true; - } + if (sensor) + sensor->Release(); // release the sensor handle - return true; + return success; } -// Removes an existing profile. Returns true if the profile was found and deleted -// and returns false otherwise. -bool ProfileManager::Delete(const Profile* profile) +//----------------------------------------------------------------------------- +bool Profile::LoadUser(JSON* root, + const char* user, + const char* model_name, + const char* device_serial) { - Lock::Locker lockScope(&ProfileLock); + if (user == NULL) + return false; - if (OVR_strcmp(profile->Name, "default") == 0) - return false; // don't delete a default profile + // 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 (CacheDevice == Profile_Unknown) - LoadCache(profile->Type); + if (device_serial) + { + tag_names[num_tags] = "Serial"; + tags[num_tags] = device_serial; + num_tags++; + } - // Look for the existence of this profile - for (unsigned int i=0; i<ProfileCache.GetSize(); i++) - { - if (OVR_strcmp(profile->Name, ProfileCache[i]->Name) == 0) - { - if (OVR_strcmp(profile->Name, DefaultProfile) == 0) - DefaultProfile.Clear(); - - ProfileCache.RemoveAt(i); - Changed = true; - return true; + // 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); + } + } + } } } - return false; -} + if (user_found) + SetValue(OVR_KEY_USER, user); + return user_found; +} //----------------------------------------------------------------------------- -// ***** Profile - -Profile::Profile(ProfileType device, const char* name) +bool Profile::LoadProfile(JSON* root, + const char* user, + const char* device_model, + const char* device_serial) { - Type = device; - Gender = Gender_Unspecified; - PlayerHeight = 1.778f; // 5'10" inch man - IPD = 0.064f; - OVR_strcpy(Name, MaxNameLen, name); + if (!LoadUser(root, user, device_model, device_serial)) + return false; + + return true; } -bool Profile::ParseProperty(const char* prop, const char* sval) +//----------------------------------------------------------------------------- +char* Profile::GetValue(const char* key, char* val, int val_length) const { - if (OVR_strcmp(prop, "Name") == 0) + JSON* value = NULL; + if (ValMap.Get(key, &value)) { - OVR_strcpy(Name, MaxNameLen, sval); - return true; + OVR_strcpy(val, val_length, value->Value.ToCStr()); + return val; } - else if (OVR_strcmp(prop, "Gender") == 0) + else { - if (OVR_strcmp(sval, "Male") == 0) - Gender = Gender_Male; - else if (OVR_strcmp(sval, "Female") == 0) - Gender = Gender_Female; - else - Gender = Gender_Unspecified; - - return true; + val[0] = 0; + return NULL; } - else if (OVR_strcmp(prop, "PlayerHeight") == 0) +} + +//----------------------------------------------------------------------------- +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)) { - PlayerHeight = (float)atof(sval); - return true; + TempVal = value->Value; + return TempVal.ToCStr(); } - else if (OVR_strcmp(prop, "IPD") == 0) + else { - IPD = (float)atof(sval); - return true; + return NULL; } - - return false; } +//----------------------------------------------------------------------------- +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; +} -// Computes the eye height from the metric head height -float Profile::GetEyeHeight() +//----------------------------------------------------------------------------- +bool Profile::GetBoolValue(const char* key, bool default_val) const { - 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 (Gender == Gender_Female) - head_height = FEMALE_AVG_HEAD_HEIGHT; + JSON* value = NULL; + if (ValMap.Get(key, &value) && value->Type == JSON_Bool) + return (value->dValue != 0); else - head_height = MALE_AVG_HEAD_HEIGHT; + return default_val; +} - float skull = EYE_TO_HEADTOP_RATIO * head_height; +//----------------------------------------------------------------------------- +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 eye_height = PlayerHeight - skull; - return eye_height; +//----------------------------------------------------------------------------- +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; } //----------------------------------------------------------------------------- -// ***** HMDProfile +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; + } +} -HMDProfile::HMDProfile(ProfileType type, const char* name) : Profile(type, name) +//----------------------------------------------------------------------------- +double Profile::GetDoubleValue(const char* key, double default_val) const { - LL = 0; - LR = 0; - RL = 0; - RR = 0; + JSON* value = NULL; + if (ValMap.Get(key, &value) && value->Type == JSON_Number) + return value->dValue; + else + return default_val; } -bool HMDProfile::ParseProperty(const char* prop, const char* sval) +//----------------------------------------------------------------------------- +int Profile::GetDoubleValues(const char* key, double* values, int num_vals) const { - if (OVR_strcmp(prop, "LL") == 0) + JSON* value = NULL; + if (ValMap.Get(key, &value) && value->Type == JSON_Array) { - LL = atoi(sval); - return true; + 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 if (OVR_strcmp(prop, "LR") == 0) + else { - LR = atoi(sval); - return true; + return 0; } - else if (OVR_strcmp(prop, "RL") == 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) { - RL = atoi(sval); - return true; + 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 if (OVR_strcmp(prop, "RR") == 0) + else { - RR = atoi(sval); - return true; + value = JSON::CreateString(val); + value->Name = key; + + Values.PushBack(value); + ValMap.Set(key, value); } - - return Profile::ParseProperty(prop, sval); } -Profile* HMDProfile::Clone() const +//----------------------------------------------------------------------------- +void Profile::SetBoolValue(const char* key, bool val) { - HMDProfile* profile = new HMDProfile(*this); - return profile; + 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); + } } //----------------------------------------------------------------------------- -// ***** RiftDK1Profile +void Profile::SetIntValue(const char* key, int val) +{ + SetDoubleValue(key, val); +} -RiftDK1Profile::RiftDK1Profile(const char* name) : HMDProfile(Profile_RiftDK1, name) +//----------------------------------------------------------------------------- +void Profile::SetFloatValue(const char* key, float val) { - EyeCups = EyeCup_A; + SetDoubleValue(key, val); } -bool RiftDK1Profile::ParseProperty(const char* prop, const char* sval) +//----------------------------------------------------------------------------- +void Profile::SetFloatValues(const char* key, const float* vals, int num_vals) { - if (OVR_strcmp(prop, "EyeCup") == 0) + JSON* value = NULL; + int val_count = 0; + if (ValMap.Get(key, &value)) { - switch (sval[0]) + if (value->Type == JSON_Array) { - case 'C': EyeCups = EyeCup_C; break; - case 'B': EyeCups = EyeCup_B; break; - default: EyeCups = EyeCup_A; break; + // 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? } - return true; } - - return HMDProfile::ParseProperty(prop, sval); -} + else + { + value = JSON::CreateArray(); + value->Name = key; -Profile* RiftDK1Profile::Clone() const -{ - RiftDK1Profile* profile = new RiftDK1Profile(*this); - return profile; + Values.PushBack(value); + ValMap.Set(key, value); + } + + for (val_count; val_count < num_vals; val_count++) + value->AddArrayNumber(vals[val_count]); } //----------------------------------------------------------------------------- -// ***** RiftDKHDProfile - -RiftDKHDProfile::RiftDKHDProfile(const char* name) : HMDProfile(Profile_RiftDKHD, name) +void Profile::SetDoubleValue(const char* key, double val) { - EyeCups = EyeCup_A; + 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); + } } -bool RiftDKHDProfile::ParseProperty(const char* prop, const char* sval) +//----------------------------------------------------------------------------- +void Profile::SetDoubleValues(const char* key, const double* vals, int num_vals) { - if (OVR_strcmp(prop, "EyeCup") == 0) + JSON* value = NULL; + int val_count = 0; + if (ValMap.Get(key, &value)) { - switch (sval[0]) + if (value->Type == JSON_Array) { - case 'C': EyeCups = EyeCup_C; break; - case 'B': EyeCups = EyeCup_B; break; - default: EyeCups = EyeCup_A; break; + // 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? } - return true; } - - return HMDProfile::ParseProperty(prop, sval); -} + else + { + value = JSON::CreateArray(); + value->Name = key; -Profile* RiftDKHDProfile::Clone() const -{ - RiftDKHDProfile* profile = new RiftDKHDProfile(*this); - return profile; + Values.PushBack(value); + ValMap.Set(key, value); + } + + for (val_count; val_count < num_vals; val_count++) + value->AddArrayNumber(vals[val_count]); } } // OVR |