ovr_sdk

diff LibOVR/Src/OVR_Profile.cpp @ 0:1b39a1b46319

initial 0.4.4
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 14 Jan 2015 06:51:16 +0200
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/LibOVR/Src/OVR_Profile.cpp	Wed Jan 14 06:51:16 2015 +0200
     1.3 @@ -0,0 +1,1627 @@
     1.4 +/************************************************************************************
     1.5 +
     1.6 +PublicHeader:   None
     1.7 +Filename    :   OVR_Profile.cpp
     1.8 +Content     :   Structs and functions for loading and storing device profile settings
     1.9 +Created     :   February 14, 2013
    1.10 +Notes       :
    1.11 +   
    1.12 +   Profiles are used to store per-user settings that can be transferred and used
    1.13 +   across multiple applications.  For example, player IPD can be configured once 
    1.14 +   and reused for a unified experience across games.  Configuration and saving of profiles
    1.15 +   can be accomplished in game via the Profile API or by the official Oculus Configuration
    1.16 +   Utility.
    1.17 +
    1.18 +Copyright   :   Copyright 2014 Oculus VR, LLC All Rights reserved.
    1.19 +
    1.20 +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); 
    1.21 +you may not use the Oculus VR Rift SDK except in compliance with the License, 
    1.22 +which is provided at the time of installation or download, or which 
    1.23 +otherwise accompanies this software in either electronic or hard copy form.
    1.24 +
    1.25 +You may obtain a copy of the License at
    1.26 +
    1.27 +http://www.oculusvr.com/licenses/LICENSE-3.2 
    1.28 +
    1.29 +Unless required by applicable law or agreed to in writing, the Oculus VR SDK 
    1.30 +distributed under the License is distributed on an "AS IS" BASIS,
    1.31 +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    1.32 +See the License for the specific language governing permissions and
    1.33 +limitations under the License.
    1.34 +
    1.35 +************************************************************************************/
    1.36 +
    1.37 +#include "OVR_Profile.h"
    1.38 +#include "OVR_JSON.h"
    1.39 +#include "Kernel/OVR_SysFile.h"
    1.40 +#include "Kernel/OVR_Allocator.h"
    1.41 +#include "OVR_Stereo.h"
    1.42 +
    1.43 +#ifdef OVR_OS_WIN32
    1.44 +#define WIN32_LEAN_AND_MEAN
    1.45 +#include <Windows.h>
    1.46 +#include <Shlobj.h>
    1.47 +#elif defined(OVR_OS_MS) // Other Microsoft OSs
    1.48 +// Nothing, thanks.
    1.49 +#else
    1.50 +#include <dirent.h>
    1.51 +#include <sys/stat.h>
    1.52 +
    1.53 +#ifdef OVR_OS_LINUX
    1.54 +#include <unistd.h>
    1.55 +#include <pwd.h>
    1.56 +#endif
    1.57 +
    1.58 +#endif
    1.59 +
    1.60 +#define PROFILE_VERSION 2.0
    1.61 +#define MAX_PROFILE_MAJOR_VERSION 2
    1.62 +#define MAX_DEVICE_PROFILE_MAJOR_VERSION 1
    1.63 +
    1.64 +
    1.65 +namespace OVR {
    1.66 +
    1.67 +
    1.68 +//-----------------------------------------------------------------------------
    1.69 +// ProfileDeviceKey
    1.70 +
    1.71 +ProfileDeviceKey::ProfileDeviceKey(const HMDInfo* info) :
    1.72 +    Valid(false)
    1.73 +{
    1.74 +    if (info)
    1.75 +    {
    1.76 +        PrintedSerial = info->PrintedSerial;
    1.77 +        ProductName = SanitizeProductName(info->ProductName);
    1.78 +        ProductId = info->ProductId;
    1.79 +        HmdType = info->HmdType;
    1.80 +
    1.81 +        if (ProductId != 0)
    1.82 +        {
    1.83 +            Valid = true;
    1.84 +        }
    1.85 +    }
    1.86 +    else
    1.87 +    {
    1.88 +        ProductId = 0;
    1.89 +        HmdType = HmdType_None;
    1.90 +    }
    1.91 +}
    1.92 +
    1.93 +String ProfileDeviceKey::SanitizeProductName(String productName)
    1.94 +{
    1.95 +    String result;
    1.96 +
    1.97 +    if (!productName.IsEmpty())
    1.98 +    {
    1.99 +        const char* product_name = productName.ToCStr();
   1.100 +
   1.101 +        // First strip off "Oculus"
   1.102 +        const char* oculus = strstr(product_name, "Oculus ");
   1.103 +        if (oculus)
   1.104 +        {
   1.105 +            product_name = oculus + OVR_strlen("Oculus ");
   1.106 +        }
   1.107 +
   1.108 +        // And remove spaces from the name
   1.109 +        for (const char* s = product_name; *s != 0; s++)
   1.110 +        {
   1.111 +            if (*s != ' ')
   1.112 +            {
   1.113 +                result.AppendChar(*s);
   1.114 +            }
   1.115 +        }
   1.116 +    }
   1.117 +
   1.118 +    return result;
   1.119 +}
   1.120 +
   1.121 +
   1.122 +
   1.123 +//-----------------------------------------------------------------------------
   1.124 +// Returns the pathname of the JSON file containing the stored profiles
   1.125 +String GetBaseOVRPath(bool create_dir)
   1.126 +{
   1.127 +    String path;
   1.128 +
   1.129 +#if defined(OVR_OS_WIN32)
   1.130 +
   1.131 +    TCHAR data_path[MAX_PATH];
   1.132 +    SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, NULL, 0, data_path);
   1.133 +    path = String(data_path);
   1.134 +    
   1.135 +    path += "/Oculus";
   1.136 +
   1.137 +    if (create_dir)
   1.138 +    {   // Create the Oculus directory if it doesn't exist
   1.139 +        WCHAR wpath[128];
   1.140 +        OVR::UTF8Util::DecodeString(wpath, path.ToCStr());
   1.141 +
   1.142 +        DWORD attrib = GetFileAttributes(wpath);
   1.143 +        bool exists = attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY);
   1.144 +        if (!exists)
   1.145 +        {   
   1.146 +            CreateDirectory(wpath, NULL);
   1.147 +        }
   1.148 +    }
   1.149 +
   1.150 +#elif defined(OVR_OS_OS) // Other Microsoft OSs
   1.151 +
   1.152 +    // TODO: figure this out.
   1.153 +    OVR_UNUSED ( create_dir );
   1.154 +    path = "";
   1.155 +        
   1.156 +#elif defined(OVR_OS_MAC)
   1.157 +
   1.158 +    const char* home = getenv("HOME");
   1.159 +    path = home;
   1.160 +    path += "/Library/Preferences/Oculus";
   1.161 +
   1.162 +    if (create_dir)
   1.163 +    {   // Create the Oculus directory if it doesn't exist
   1.164 +        DIR* dir = opendir(path);
   1.165 +        if (dir == NULL)
   1.166 +        {
   1.167 +            mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
   1.168 +        }
   1.169 +        else
   1.170 +        {
   1.171 +            closedir(dir);
   1.172 +        }
   1.173 +    }
   1.174 +
   1.175 +#else
   1.176 +
   1.177 +    const char* home = getenv("HOME");
   1.178 +    path = home;
   1.179 +    path += "/.config/Oculus";
   1.180 +
   1.181 +    if (create_dir)
   1.182 +    {   // Create the Oculus directory if it doesn't exist
   1.183 +        DIR* dir = opendir(path);
   1.184 +        if (dir == NULL)
   1.185 +        {
   1.186 +            mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
   1.187 +        }
   1.188 +        else
   1.189 +        {
   1.190 +            closedir(dir);
   1.191 +        }
   1.192 +    }
   1.193 +
   1.194 +#endif
   1.195 +
   1.196 +    return path;
   1.197 +}
   1.198 +
   1.199 +String ProfileManager::GetProfilePath()
   1.200 +{
   1.201 +    return BasePath + "/ProfileDB.json";
   1.202 +}
   1.203 +
   1.204 +static JSON* FindTaggedData(JSON* data, const char** tag_names, const char** qtags, int num_qtags)
   1.205 +{
   1.206 +    if (data == NULL || !(data->Name == "TaggedData") || data->Type != JSON_Array)
   1.207 +        return NULL;
   1.208 +
   1.209 +    JSON* tagged_item = data->GetFirstItem();
   1.210 +    while (tagged_item)
   1.211 +    {
   1.212 +        JSON* tags = tagged_item->GetItemByName("tags");
   1.213 +        if (tags->Type == JSON_Array && num_qtags == tags->GetArraySize())
   1.214 +        {   // Check for a full tag match on each item
   1.215 +            int num_matches = 0;
   1.216 +            
   1.217 +            for (int k=0; k<num_qtags; k++)
   1.218 +            {
   1.219 +                JSON* tag = tags->GetFirstItem();
   1.220 +                while (tag)
   1.221 +                {
   1.222 +                    JSON* tagval = tag->GetFirstItem();
   1.223 +                    if (tagval && tagval->Name == tag_names[k])
   1.224 +                    {
   1.225 +                        if (tagval->Value == qtags[k])
   1.226 +                            num_matches++;
   1.227 +                        break;
   1.228 +                    }
   1.229 +                    tag = tags->GetNextItem(tag);
   1.230 +                }
   1.231 +            }
   1.232 +
   1.233 +            // if all tags were matched then copy the values into this Profile
   1.234 +            if (num_matches == num_qtags)
   1.235 +            {
   1.236 +                JSON* vals = tagged_item->GetItemByName("vals");
   1.237 +                return vals;
   1.238 +            }
   1.239 +        }
   1.240 +
   1.241 +        tagged_item = data->GetNextItem(tagged_item);
   1.242 +    }
   1.243 +
   1.244 +    return NULL;
   1.245 +}
   1.246 +
   1.247 +static void FilterTaggedData(JSON* data, const char* tag_name, const char* qtag, Array<JSON*>& items)
   1.248 +{
   1.249 +    if (data == NULL || !(data->Name == "TaggedData") || data->Type != JSON_Array)
   1.250 +        return;
   1.251 +
   1.252 +    JSON* tagged_item = data->GetFirstItem();
   1.253 +    while (tagged_item)
   1.254 +    {
   1.255 +        JSON* tags = tagged_item->GetItemByName("tags");
   1.256 +        if (tags->Type == JSON_Array)
   1.257 +        {   // Check for a tag match on the requested tag
   1.258 +            
   1.259 +            JSON* tag = tags->GetFirstItem();
   1.260 +            while (tag)
   1.261 +            {
   1.262 +                JSON* tagval = tag->GetFirstItem();
   1.263 +                if (tagval && tagval->Name == tag_name)
   1.264 +                {
   1.265 +                    if (tagval->Value == qtag)
   1.266 +                    {   // Add this item to the output list
   1.267 +                        items.PushBack(tagged_item);
   1.268 +                    }
   1.269 +                    break;
   1.270 +                }
   1.271 +                tag = tags->GetNextItem(tag);
   1.272 +            }
   1.273 +        }
   1.274 +
   1.275 +        tagged_item = data->GetNextItem(tagged_item);
   1.276 +    }
   1.277 +}
   1.278 +
   1.279 +
   1.280 +//-----------------------------------------------------------------------------
   1.281 +// ***** ProfileManager
   1.282 +
   1.283 +template<> ProfileManager* OVR::SystemSingletonBase<ProfileManager>::SlowGetInstance()
   1.284 +{
   1.285 +    static OVR::Lock lock;
   1.286 +    OVR::Lock::Locker locker(&lock);
   1.287 +    if (!SingletonInstance) SingletonInstance = new ProfileManager(true);
   1.288 +    return SingletonInstance;
   1.289 +}
   1.290 +
   1.291 +ProfileManager::ProfileManager(bool sys_register) :
   1.292 +    Changed(false)
   1.293 +{
   1.294 +    // Attempt to get the base path automatically, but this may fail
   1.295 +    BasePath = GetBaseOVRPath(false);
   1.296 +
   1.297 +    if (sys_register)
   1.298 +        PushDestroyCallbacks();
   1.299 +}
   1.300 +
   1.301 +ProfileManager::~ProfileManager()
   1.302 +{
   1.303 +    ClearProfileData();
   1.304 +}
   1.305 +
   1.306 +void ProfileManager::OnSystemDestroy()
   1.307 +{
   1.308 +    delete this;
   1.309 +}
   1.310 +
   1.311 +// In the service process it is important to set the base path because this cannot be detected automatically
   1.312 +void ProfileManager::SetBasePath(String basePath)
   1.313 +{
   1.314 +    if (basePath != BasePath)
   1.315 +    {
   1.316 +        BasePath = basePath;
   1.317 +        LoadCache(false);
   1.318 +    }
   1.319 +}
   1.320 +
   1.321 +// Clear the local profile cache
   1.322 +void ProfileManager::ClearProfileData()
   1.323 +{
   1.324 +    Lock::Locker lockScope(&ProfileLock);
   1.325 +
   1.326 +    ProfileCache.Clear();
   1.327 +    Changed = false;
   1.328 +}
   1.329 +
   1.330 +// Serializes the profiles to disk.
   1.331 +void ProfileManager::Save()
   1.332 +{
   1.333 +    Lock::Locker lockScope(&ProfileLock);
   1.334 +
   1.335 +    if (ProfileCache == NULL)
   1.336 +        return;
   1.337 +
   1.338 +    // Save the profile to disk
   1.339 +    BasePath = GetBaseOVRPath(true);  // create the base directory if it doesn't exist
   1.340 +    String path = GetProfilePath();
   1.341 +    ProfileCache->Save(path);
   1.342 +    Changed = false;
   1.343 +}
   1.344 +
   1.345 +// Returns a profile with all system default values
   1.346 +Profile* ProfileManager::GetDefaultProfile(HmdTypeEnum device)
   1.347 +{
   1.348 +    // In the absence of any data, set some reasonable profile defaults.
   1.349 +    // However, this is not future proof and developers should still
   1.350 +    // provide reasonable default values for queried fields.
   1.351 +    
   1.352 +    // Biometric data
   1.353 +    Profile* profile = CreateProfile();
   1.354 +    profile->SetValue(OVR_KEY_USER,               "default");
   1.355 +    profile->SetValue(OVR_KEY_NAME,               "Default");
   1.356 +    profile->SetValue(OVR_KEY_GENDER,             OVR_DEFAULT_GENDER);
   1.357 +    profile->SetFloatValue(OVR_KEY_PLAYER_HEIGHT, OVR_DEFAULT_PLAYER_HEIGHT);
   1.358 +    profile->SetFloatValue(OVR_KEY_EYE_HEIGHT,    OVR_DEFAULT_EYE_HEIGHT);
   1.359 +    profile->SetFloatValue(OVR_KEY_IPD,           OVR_DEFAULT_IPD);
   1.360 +    float half_ipd[2] = { OVR_DEFAULT_IPD / 2, OVR_DEFAULT_IPD / 2 };
   1.361 +    profile->SetFloatValues(OVR_KEY_EYE_TO_NOSE_DISTANCE, half_ipd, 2);
   1.362 +    float dist[2] = {OVR_DEFAULT_NECK_TO_EYE_HORIZONTAL, OVR_DEFAULT_NECK_TO_EYE_VERTICAL};
   1.363 +    profile->SetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, dist, 2);
   1.364 +    
   1.365 +    // Device specific data
   1.366 +    if (device != HmdType_None)
   1.367 +    {
   1.368 +        if (device == HmdType_CrystalCoveProto || device == HmdType_DK2)
   1.369 +        {
   1.370 +            profile->SetValue("EyeCup", "A");
   1.371 +            profile->SetIntValue(OVR_KEY_EYE_RELIEF_DIAL, OVR_DEFAULT_EYE_RELIEF_DIAL);
   1.372 +
   1.373 +            // TODO: These defaults are a little bogus and designed for continuity with 0.3
   1.374 +            // eye-relief values.  We need better measurement-based numbers in future releases
   1.375 +            float max_eye_plate[2] = { 0.01965f + 0.018f, 0.01965f + 0.018f };
   1.376 +            profile->SetFloatValues(OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE, max_eye_plate, 2);
   1.377 +        }
   1.378 +        else
   1.379 +        {   // DK1 and DKHD variants
   1.380 +            profile->SetValue("EyeCup", "A");
   1.381 +            profile->SetIntValue(OVR_KEY_EYE_RELIEF_DIAL, OVR_DEFAULT_EYE_RELIEF_DIAL);
   1.382 +
   1.383 +            // TODO: These defaults are a little bogus and designed for continuity with 0.3
   1.384 +            // DK1 distortion.  We need better measurement-based numbers in future releases
   1.385 +            float max_eye_plate[2] = { 0.02357f + 0.017f, 0.02357f + 0.017f };
   1.386 +            profile->SetFloatValues(OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE, max_eye_plate, 2);
   1.387 +        }
   1.388 +    }
   1.389 +
   1.390 +    return profile;
   1.391 +}
   1.392 +
   1.393 +//------------------------------------------------------------------------------
   1.394 +void ProfileManager::Read()
   1.395 +{
   1.396 +    LoadCache(false);
   1.397 +}
   1.398 +
   1.399 +// Populates the local profile cache.  This occurs on the first access of the profile
   1.400 +// data.  All profile operations are performed against the local cache until the
   1.401 +// ProfileManager is released or goes out of scope at which time the cache is serialized
   1.402 +// to disk.
   1.403 +void ProfileManager::LoadCache(bool create)
   1.404 +{
   1.405 +    Lock::Locker lockScope(&ProfileLock);
   1.406 +
   1.407 +    ClearProfileData();
   1.408 +
   1.409 +    String path = GetProfilePath();
   1.410 +
   1.411 +    Ptr<JSON> root = *JSON::Load(path);
   1.412 +    if (root == NULL)
   1.413 +    {   
   1.414 +        path = BasePath + "/Profiles.json";  // look for legacy profile
   1.415 +        root = *JSON::Load(path);
   1.416 +        
   1.417 +        if (root == NULL)
   1.418 +        {
   1.419 +            if (create)
   1.420 +            {   // Generate a skeleton profile database
   1.421 +                root = *JSON::CreateObject();
   1.422 +                root->AddNumberItem("Oculus Profile Version", 2.0);
   1.423 +                root->AddItem("Users", JSON::CreateArray());
   1.424 +                root->AddItem("TaggedData", JSON::CreateArray());
   1.425 +                ProfileCache = root;
   1.426 +            }
   1.427 +            
   1.428 +            return;
   1.429 +        }
   1.430 +
   1.431 +        // Verify the legacy version
   1.432 +        JSON* version_item = root->GetFirstItem();
   1.433 +        if (version_item->Name == "Oculus Profile Version")
   1.434 +        {
   1.435 +            int major = atoi(version_item->Value.ToCStr());
   1.436 +            if (major != 1)
   1.437 +                return;   // don't use the file on unsupported major version number
   1.438 +        }
   1.439 +        else
   1.440 +        {
   1.441 +            return;      // invalid file
   1.442 +        }
   1.443 +
   1.444 +        // Convert the legacy format to the new database format
   1.445 +        LoadV1Profiles(root);
   1.446 +    }
   1.447 +    else
   1.448 +    {
   1.449 +        // Verify the file format and version
   1.450 +        JSON* version_item = root->GetFirstItem();
   1.451 +        if (version_item && version_item->Name == "Oculus Profile Version")
   1.452 +        {
   1.453 +            int major = atoi(version_item->Value.ToCStr());
   1.454 +            if (major != 2)
   1.455 +                return;   // don't use the file on unsupported major version number
   1.456 +        }
   1.457 +        else
   1.458 +        {
   1.459 +            return;       // invalid file 
   1.460 +        }
   1.461 +
   1.462 +        ProfileCache = root;   // store the database contents for traversal
   1.463 +    }
   1.464 +}
   1.465 +
   1.466 +void ProfileManager::LoadV1Profiles(JSON* v1)
   1.467 +{
   1.468 +    JSON* item0 = v1->GetFirstItem();
   1.469 +    JSON* item1 = v1->GetNextItem(item0);
   1.470 +    JSON* item2 = v1->GetNextItem(item1);
   1.471 +
   1.472 +    OVR_ASSERT(item1 && item2);
   1.473 +    if(!item1 || !item2)
   1.474 +        return;
   1.475 +
   1.476 +    // Create the new profile database
   1.477 +    Ptr<JSON> root = *JSON::CreateObject();
   1.478 +    root->AddNumberItem("Oculus Profile Version", 2.0);
   1.479 +    root->AddItem("Users", JSON::CreateArray());
   1.480 +    root->AddItem("TaggedData", JSON::CreateArray());
   1.481 +    ProfileCache = root;
   1.482 +
   1.483 +    const char* default_dk1_user = item1->Value;
   1.484 +    
   1.485 +    // Read the number of profiles
   1.486 +    int   profileCount = (int)item2->dValue;
   1.487 +    JSON* profileItem  = item2;
   1.488 +
   1.489 +    for (int p=0; p<profileCount; p++)
   1.490 +    {
   1.491 +        profileItem = root->GetNextItem(profileItem);
   1.492 +        if (profileItem == NULL)
   1.493 +            break;
   1.494 +
   1.495 +        if (profileItem->Name == "Profile")
   1.496 +        {
   1.497 +            // Read the required Name field
   1.498 +            const char* profileName;
   1.499 +            JSON* item = profileItem->GetFirstItem();
   1.500 +        
   1.501 +            if (item && (item->Name == "Name"))
   1.502 +            {   
   1.503 +                profileName = item->Value;
   1.504 +            }
   1.505 +            else
   1.506 +            {
   1.507 +                return;   // invalid field
   1.508 +            }
   1.509 +            
   1.510 +            // Read the user profile fields
   1.511 +            if (CreateUser(profileName, profileName))
   1.512 +            {
   1.513 +                const char* tag_names[2] = {"User", "Product"};
   1.514 +                const char* tags[2];
   1.515 +                tags[0] = profileName;
   1.516 +
   1.517 +                Ptr<Profile> user_profile = *CreateProfile();
   1.518 +                user_profile->SetValue(OVR_KEY_NAME, profileName);
   1.519 +
   1.520 +                float neckeye[2] = { 0, 0 };
   1.521 +
   1.522 +                item = profileItem->GetNextItem(item);
   1.523 +                while (item)
   1.524 +                {
   1.525 +                    if (item->Type != JSON_Object)
   1.526 +                    {
   1.527 +                        if (item->Name == OVR_KEY_PLAYER_HEIGHT)
   1.528 +                        {   // Add an explicit eye height
   1.529 +
   1.530 +                        }
   1.531 +                        if (item->Name == "NeckEyeHori")
   1.532 +                            neckeye[0] = (float)item->dValue;
   1.533 +                        else if (item->Name == "NeckEyeVert")
   1.534 +                            neckeye[1] = (float)item->dValue;
   1.535 +                        else 
   1.536 +                            user_profile->SetValue(item);
   1.537 +                    }
   1.538 +                    else
   1.539 +                    {   
   1.540 +                        // Add the user/device tag values
   1.541 +                        const char* device_name = item->Name.ToCStr();
   1.542 +                        Ptr<Profile> device_profile = *CreateProfile();
   1.543 +
   1.544 +                        JSON* device_item = item->GetFirstItem();
   1.545 +                        while (device_item)
   1.546 +                        {
   1.547 +                            device_profile->SetValue(device_item);
   1.548 +                            device_item = item->GetNextItem(device_item);
   1.549 +                        }
   1.550 +
   1.551 +                        tags[1] = device_name;
   1.552 +                        SetTaggedProfile(tag_names, tags, 2, device_profile);
   1.553 +                    }
   1.554 +
   1.555 +                    item = profileItem->GetNextItem(item);
   1.556 +                }
   1.557 +
   1.558 +                // Add an explicit eye-height field
   1.559 +                float player_height = user_profile->GetFloatValue(OVR_KEY_PLAYER_HEIGHT,
   1.560 +                                                                  OVR_DEFAULT_PLAYER_HEIGHT);
   1.561 +                if (player_height > 0)
   1.562 +                {
   1.563 +                    char gender[16];
   1.564 +                    user_profile->GetValue(OVR_KEY_GENDER, gender, 16);
   1.565 +        
   1.566 +                    const float EYE_TO_HEADTOP_RATIO =   0.44538f;
   1.567 +                    const float MALE_AVG_HEAD_HEIGHT =   0.232f;
   1.568 +                    const float FEMALE_AVG_HEAD_HEIGHT = 0.218f;
   1.569 +     
   1.570 +                    // compute distance from top of skull to the eye
   1.571 +                    float head_height;
   1.572 +                    if (OVR_strcmp(gender, "Female") == 0)
   1.573 +                        head_height = FEMALE_AVG_HEAD_HEIGHT;
   1.574 +                    else
   1.575 +                        head_height = MALE_AVG_HEAD_HEIGHT;
   1.576 +
   1.577 +                    float skull = EYE_TO_HEADTOP_RATIO * head_height;
   1.578 +                    float eye_height = player_height - skull;
   1.579 +
   1.580 +                    user_profile->SetFloatValue(OVR_KEY_EYE_HEIGHT, eye_height);
   1.581 +                }
   1.582 +
   1.583 +                // Convert NeckEye values to an array
   1.584 +                if (neckeye[0] > 0 && neckeye[1] > 0)
   1.585 +                    user_profile->SetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, neckeye, 2);
   1.586 +
   1.587 +                // Add the user tag values
   1.588 +                SetTaggedProfile(tag_names, tags, 1, user_profile);
   1.589 +            }
   1.590 +        }
   1.591 +    }
   1.592 +
   1.593 +    // since V1 profiles were only for DK1, the assign the user to all DK1's
   1.594 +    const char* tag_names[1] = { "Product" };
   1.595 +    const char* tags[1] = { "RiftDK1" };
   1.596 +    Ptr<Profile> product_profile = *CreateProfile();
   1.597 +    product_profile->SetValue("DefaultUser", default_dk1_user);
   1.598 +    SetTaggedProfile(tag_names, tags, 1, product_profile);
   1.599 +}
   1.600 +
   1.601 +// Returns the number of stored profiles for this device type
   1.602 +int ProfileManager::GetUserCount()
   1.603 +{
   1.604 +    Lock::Locker lockScope(&ProfileLock);
   1.605 +
   1.606 +    if (ProfileCache == NULL)
   1.607 +    {   // Load the cache
   1.608 +        LoadCache(false);
   1.609 +        if (ProfileCache == NULL)
   1.610 +            return 0;
   1.611 +    }
   1.612 +
   1.613 +    JSON* users = ProfileCache->GetItemByName("Users");
   1.614 +    if (users == NULL)
   1.615 +        return 0;
   1.616 +
   1.617 +    return users->GetItemCount();
   1.618 +}
   1.619 +
   1.620 +bool ProfileManager::CreateUser(const char* user, const char* name)
   1.621 +{
   1.622 +    Lock::Locker lockScope(&ProfileLock);
   1.623 +
   1.624 +    if (ProfileCache == NULL)
   1.625 +    {   // Load the cache
   1.626 +        LoadCache(true);
   1.627 +        if (ProfileCache == NULL)
   1.628 +            return false;
   1.629 +    }
   1.630 +
   1.631 +    JSON* users = ProfileCache->GetItemByName("Users");
   1.632 +    if (users == NULL)
   1.633 +    {   // Generate the User section
   1.634 +        users = JSON::CreateArray();
   1.635 +        ProfileCache->AddItem("Users", users);
   1.636 +//TODO: Insert this before the TaggedData
   1.637 +    }
   1.638 +
   1.639 +    // Search for the pre-existence of this user
   1.640 +    JSON* user_item = users->GetFirstItem();
   1.641 +    int index = 0;
   1.642 +    while (user_item)
   1.643 +    {
   1.644 +        JSON* userid = user_item->GetItemByName("User");
   1.645 +        int compare = OVR_strcmp(user, userid->Value);
   1.646 +        if (compare == 0)
   1.647 +        {   // The user already exists so simply update the fields
   1.648 +            JSON* name_item = user_item->GetItemByName("Name");
   1.649 +            if (name_item && OVR_strcmp(name, name_item->Value) != 0)
   1.650 +            {
   1.651 +                name_item->Value = name;
   1.652 +                Changed = true;
   1.653 +            }
   1.654 +            return true;
   1.655 +        }
   1.656 +        else if (compare < 0)
   1.657 +        {   // A new user should be placed before this item
   1.658 +            break;
   1.659 +        }
   1.660 +        
   1.661 +        user_item = users->GetNextItem(user_item);
   1.662 +        index++;
   1.663 +    }
   1.664 +
   1.665 +    // Create and fill the user struct
   1.666 +    JSON* new_user = JSON::CreateObject();
   1.667 +    new_user->AddStringItem(OVR_KEY_USER, user);
   1.668 +    new_user->AddStringItem(OVR_KEY_NAME, name);
   1.669 +    // user_item->AddStringItem("Password", password);
   1.670 +
   1.671 +    if (user_item == NULL)
   1.672 +        users->AddArrayElement(new_user);
   1.673 +    else
   1.674 +        users->InsertArrayElement(index, new_user);
   1.675 +
   1.676 +    Changed = true;
   1.677 +    return true;
   1.678 +}
   1.679 +
   1.680 +bool ProfileManager::HasUser(const char* user)
   1.681 +{
   1.682 +	Lock::Locker lockScope(&ProfileLock);
   1.683 +
   1.684 +	if (ProfileCache == NULL)
   1.685 +	{   // Load the cache
   1.686 +		LoadCache(false);
   1.687 +		if (ProfileCache == NULL)
   1.688 +			return false;
   1.689 +	}
   1.690 +
   1.691 +	JSON* users = ProfileCache->GetItemByName("Users");
   1.692 +	if (users == NULL)
   1.693 +		return false;
   1.694 +
   1.695 +	// Remove this user from the User table
   1.696 +	JSON* user_item = users->GetFirstItem();
   1.697 +	while (user_item)
   1.698 +	{
   1.699 +		JSON* userid = user_item->GetItemByName("User");
   1.700 +		if (OVR_strcmp(user, userid->Value) == 0)
   1.701 +		{   
   1.702 +			return true;
   1.703 +		}
   1.704 +
   1.705 +		user_item = users->GetNextItem(user_item);
   1.706 +	}
   1.707 +
   1.708 +	return false;
   1.709 +}
   1.710 +
   1.711 +// Returns the user id of a specific user in the list.  The returned 
   1.712 +// memory is locally allocated and should not be stored or deleted.  Returns NULL
   1.713 +// if the index is invalid
   1.714 +const char* ProfileManager::GetUser(unsigned int index)
   1.715 +{
   1.716 +    Lock::Locker lockScope(&ProfileLock);
   1.717 +
   1.718 +    if (ProfileCache == NULL)
   1.719 +    {   // Load the cache
   1.720 +        LoadCache(false);
   1.721 +        if (ProfileCache == NULL)
   1.722 +            return NULL;
   1.723 +    }
   1.724 +
   1.725 +    JSON* users = ProfileCache->GetItemByName("Users");
   1.726 +    
   1.727 +    if (users && index < users->GetItemCount())
   1.728 +    {
   1.729 +        JSON* user_item = users->GetItemByIndex(index);
   1.730 +        if (user_item)
   1.731 +        {
   1.732 +            JSON* user = user_item->GetFirstItem();
   1.733 +            if (user)
   1.734 +            {
   1.735 +                JSON* userid = user_item->GetItemByName(OVR_KEY_USER);
   1.736 +                if (userid)
   1.737 +                    return userid->Value.ToCStr();
   1.738 +            }
   1.739 +        }
   1.740 +    }
   1.741 +    
   1.742 +
   1.743 +    return NULL;
   1.744 +}
   1.745 +
   1.746 +bool ProfileManager::RemoveUser(const char* user)
   1.747 +{
   1.748 +    Lock::Locker lockScope(&ProfileLock);
   1.749 +
   1.750 +    if (ProfileCache == NULL)
   1.751 +    {   // Load the cache
   1.752 +        LoadCache(false);
   1.753 +        if (ProfileCache == NULL)
   1.754 +            return true;
   1.755 +    }
   1.756 +
   1.757 +    JSON* users = ProfileCache->GetItemByName("Users");
   1.758 +    if (users == NULL)
   1.759 +        return true;
   1.760 +
   1.761 +    // Remove this user from the User table
   1.762 +    JSON* user_item = users->GetFirstItem();
   1.763 +    while (user_item)
   1.764 +    {
   1.765 +        JSON* userid = user_item->GetItemByName("User");
   1.766 +        if (OVR_strcmp(user, userid->Value) == 0)
   1.767 +        {   // Delete the user entry
   1.768 +            user_item->RemoveNode();
   1.769 +            user_item->Release();
   1.770 +            Changed = true;
   1.771 +            break;
   1.772 +        }
   1.773 +        
   1.774 +        user_item = users->GetNextItem(user_item);
   1.775 +    }
   1.776 +
   1.777 +    // Now remove all data entries with this user tag
   1.778 +    JSON* tagged_data = ProfileCache->GetItemByName("TaggedData");
   1.779 +    Array<JSON*> user_items;
   1.780 +    FilterTaggedData(tagged_data, "User", user, user_items);
   1.781 +    for (unsigned int i=0; i<user_items.GetSize(); i++)
   1.782 +    {
   1.783 +        user_items[i]->RemoveNode();
   1.784 +        user_items[i]->Release();
   1.785 +        Changed = true;
   1.786 +    }
   1.787 + 
   1.788 +    return Changed;
   1.789 +}
   1.790 +
   1.791 +Profile* ProfileManager::CreateProfile()
   1.792 +{
   1.793 +    Profile* profile = new Profile(BasePath);
   1.794 +    return profile;
   1.795 +}
   1.796 +
   1.797 +const char* ProfileManager::GetDefaultUser(const ProfileDeviceKey& deviceKey)
   1.798 +{
   1.799 +    const char* product_str = deviceKey.ProductName.IsEmpty() ? NULL : deviceKey.ProductName.ToCStr();
   1.800 +    const char* serial_str = deviceKey.PrintedSerial.IsEmpty() ? NULL : deviceKey.PrintedSerial.ToCStr();
   1.801 +
   1.802 +    return GetDefaultUser(product_str, serial_str);
   1.803 +}
   1.804 +
   1.805 +// Returns the name of the profile that is marked as the current default user.
   1.806 +const char* ProfileManager::GetDefaultUser(const char* product, const char* serial)
   1.807 +{
   1.808 +    const char* tag_names[2] = {"Product", "Serial"};
   1.809 +    const char* tags[2];
   1.810 +
   1.811 +    if (product && serial)
   1.812 +    {
   1.813 +        tags[0] = product;
   1.814 +        tags[1] = serial;
   1.815 +        // Look for a default user on this specific device
   1.816 +        Ptr<Profile> p = *GetTaggedProfile(tag_names, tags, 2);
   1.817 +        if (p == NULL)
   1.818 +        {   // Look for a default user on this product
   1.819 +            p = *GetTaggedProfile(tag_names, tags, 1);
   1.820 +        }
   1.821 +
   1.822 +        if (p)
   1.823 +        {   
   1.824 +            const char* user = p->GetValue("DefaultUser");
   1.825 +            if (user != NULL && user[0] != 0)
   1.826 +            {
   1.827 +                TempBuff = user;
   1.828 +                return TempBuff.ToCStr();
   1.829 +            }
   1.830 +        }
   1.831 +    }
   1.832 +
   1.833 +    return NULL;
   1.834 +}
   1.835 +
   1.836 +//-----------------------------------------------------------------------------
   1.837 +bool ProfileManager::SetDefaultUser(const ProfileDeviceKey& deviceKey, const char* user)
   1.838 +{
   1.839 +    const char* tag_names[2] = {"Product", "Serial"};
   1.840 +    const char* tags[2];
   1.841 +
   1.842 +    const char* product_str = deviceKey.ProductName.IsEmpty() ? NULL : deviceKey.ProductName.ToCStr();
   1.843 +    const char* serial_str = deviceKey.PrintedSerial.IsEmpty() ? NULL : deviceKey.PrintedSerial.ToCStr();
   1.844 +
   1.845 +    if (product_str && serial_str)
   1.846 +    {
   1.847 +        tags[0] = product_str;
   1.848 +        tags[1] = serial_str;
   1.849 +
   1.850 +        Ptr<Profile> p = *CreateProfile();
   1.851 +        p->SetValue("DefaultUser", user);
   1.852 +        return SetTaggedProfile(tag_names, tags, 2, p);
   1.853 +    }
   1.854 +
   1.855 +    return false;
   1.856 +}
   1.857 +
   1.858 +//-----------------------------------------------------------------------------
   1.859 +Profile* ProfileManager::GetTaggedProfile(const char** tag_names, const char** tags, int num_tags)
   1.860 +{
   1.861 +    Lock::Locker lockScope(&ProfileLock);
   1.862 +
   1.863 +    if (ProfileCache == NULL)
   1.864 +    {   // Load the cache
   1.865 +        LoadCache(false);
   1.866 +        if (ProfileCache == NULL)
   1.867 +            return NULL;
   1.868 +    }
   1.869 +
   1.870 +    JSON* tagged_data = ProfileCache->GetItemByName("TaggedData");
   1.871 +    OVR_ASSERT(tagged_data);
   1.872 +    if (tagged_data == NULL)
   1.873 +        return NULL;
   1.874 +    
   1.875 +    Profile* profile = new Profile(BasePath);
   1.876 +    
   1.877 +    JSON* vals = FindTaggedData(tagged_data, tag_names, tags, num_tags);
   1.878 +    if (vals)
   1.879 +    {   
   1.880 +        JSON* item = vals->GetFirstItem();
   1.881 +        while (item)
   1.882 +        {
   1.883 +            //printf("Add %s, %s\n", item->Name.ToCStr(), item->Value.ToCStr());
   1.884 +            //profile->Settings.Set(item->Name, item->Value);
   1.885 +            profile->SetValue(item);
   1.886 +            item = vals->GetNextItem(item);
   1.887 +        }
   1.888 +
   1.889 +        return profile;
   1.890 +    }
   1.891 +    else
   1.892 +    {
   1.893 +        profile->Release();
   1.894 +        return NULL;
   1.895 +    }
   1.896 +}
   1.897 +
   1.898 +//-----------------------------------------------------------------------------
   1.899 +bool ProfileManager::SetTaggedProfile(const char** tag_names, const char** tags, int num_tags, Profile* profile)
   1.900 +{
   1.901 +    Lock::Locker lockScope(&ProfileLock);
   1.902 +
   1.903 +    if (ProfileCache == NULL)
   1.904 +    {   // Load the cache
   1.905 +        LoadCache(true);
   1.906 +        if (ProfileCache == NULL)
   1.907 +            return false;  // TODO: Generate a new profile DB
   1.908 +    }
   1.909 +
   1.910 +    JSON* tagged_data = ProfileCache->GetItemByName("TaggedData");
   1.911 +    OVR_ASSERT(tagged_data);
   1.912 +    if (tagged_data == NULL)
   1.913 +        return false;
   1.914 +
   1.915 +    // Get the cached tagged data section
   1.916 +    JSON* vals = FindTaggedData(tagged_data, tag_names, tags, num_tags);
   1.917 +    if (vals == NULL)
   1.918 +    {  
   1.919 +        JSON* tagged_item = JSON::CreateObject();
   1.920 +        JSON* taglist = JSON::CreateArray();
   1.921 +        for (int i=0; i<num_tags; i++)
   1.922 +        {
   1.923 +            JSON* k = JSON::CreateObject();
   1.924 +            k->AddStringItem(tag_names[i], tags[i]);
   1.925 +            taglist->AddArrayElement(k);
   1.926 +        }
   1.927 +
   1.928 +        vals = JSON::CreateObject();
   1.929 +        
   1.930 +        tagged_item->AddItem("tags", taglist);
   1.931 +        tagged_item->AddItem("vals", vals);
   1.932 +        tagged_data->AddArrayElement(tagged_item);
   1.933 +    }
   1.934 +
   1.935 +    // Now add or update each profile setting in cache
   1.936 +    for (unsigned int i=0; i<profile->Values.GetSize(); i++)
   1.937 +    {
   1.938 +        JSON* value = profile->Values[i];
   1.939 +        
   1.940 +        bool found = false;
   1.941 +        JSON* item = vals->GetFirstItem();
   1.942 +        while (item)
   1.943 +        {
   1.944 +            if (value->Name == item->Name)
   1.945 +            {
   1.946 +                // Don't allow a pre-existing type to be overridden
   1.947 +                OVR_ASSERT(value->Type == item->Type);
   1.948 +
   1.949 +                if (value->Type == item->Type)
   1.950 +                {   // Check for the same value
   1.951 +                    if (value->Type == JSON_Array)
   1.952 +                    {   // Update each array item
   1.953 +                        if (item->GetArraySize() == value->GetArraySize())
   1.954 +                        {   // Update each value (assumed to be basic types and not array of objects)
   1.955 +                            JSON* value_element = value->GetFirstItem();
   1.956 +                            JSON* item_element = item->GetFirstItem();
   1.957 +                            while (item_element && value_element)
   1.958 +                            {
   1.959 +                                if (value_element->Type == JSON_String)
   1.960 +                                {
   1.961 +                                    if (item_element->Value != value_element->Value)
   1.962 +                                    {   // Overwrite the changed value and mark for file update
   1.963 +                                        item_element->Value = value_element->Value;
   1.964 +                                        Changed = true;
   1.965 +                                    }
   1.966 +                                }
   1.967 +                                else {
   1.968 +                                    if (item_element->dValue != value_element->dValue)
   1.969 +                                    {   // Overwrite the changed value and mark for file update
   1.970 +                                        item_element->dValue = value_element->dValue;
   1.971 +                                        Changed = true;
   1.972 +                                    }
   1.973 +                                }
   1.974 +                                
   1.975 +                                value_element = value->GetNextItem(value_element);
   1.976 +                                item_element = item->GetNextItem(item_element);
   1.977 +                            }
   1.978 +                        }
   1.979 +                        else
   1.980 +                        {   // if the array size changed, simply create a new one                            
   1.981 +// TODO: Create the new array
   1.982 +                        }
   1.983 +                    }
   1.984 +                    else if (value->Type == JSON_String)
   1.985 +                    {
   1.986 +                        if (item->Value != value->Value)
   1.987 +                        {   // Overwrite the changed value and mark for file update
   1.988 +                            item->Value = value->Value;
   1.989 +                            Changed = true;
   1.990 +                        }
   1.991 +                    }
   1.992 +                    else {
   1.993 +                        if (item->dValue != value->dValue)
   1.994 +                        {   // Overwrite the changed value and mark for file update
   1.995 +                            item->dValue = value->dValue;
   1.996 +                            Changed = true;
   1.997 +                        }
   1.998 +                    }
   1.999 +                }
  1.1000 +                else
  1.1001 +                {
  1.1002 +                    return false;
  1.1003 +                }
  1.1004 +
  1.1005 +                found = true;
  1.1006 +                break;
  1.1007 +            }
  1.1008 +            
  1.1009 +            item = vals->GetNextItem(item);
  1.1010 +        }
  1.1011 +
  1.1012 +        if (!found)
  1.1013 +        {   // Add the new value
  1.1014 +            Changed = true;
  1.1015 +
  1.1016 +            if (value->Type == JSON_String)
  1.1017 +                vals->AddStringItem(value->Name, value->Value);
  1.1018 +            else if (value->Type == JSON_Bool)
  1.1019 +                vals->AddBoolItem(value->Name, ((int)value->dValue != 0));
  1.1020 +            else if (value->Type == JSON_Number)
  1.1021 +                vals->AddNumberItem(value->Name, value->dValue);
  1.1022 +            else if (value->Type == JSON_Array)
  1.1023 +                vals->AddItem(value->Name, value->Copy());
  1.1024 +            else
  1.1025 +            {
  1.1026 +                OVR_ASSERT(false);
  1.1027 +                Changed = false;
  1.1028 +            }
  1.1029 +        }
  1.1030 +    }
  1.1031 +
  1.1032 +    return true;
  1.1033 +}
  1.1034 +
  1.1035 +//-----------------------------------------------------------------------------
  1.1036 +Profile* ProfileManager::GetDefaultUserProfile(const ProfileDeviceKey& deviceKey)
  1.1037 +{
  1.1038 +    const char* userName = GetDefaultUser(deviceKey);
  1.1039 +
  1.1040 +    Profile* profile = GetProfile(deviceKey, userName);
  1.1041 +
  1.1042 +    if (!profile)
  1.1043 +    {
  1.1044 +        profile = GetDefaultProfile(deviceKey.HmdType);
  1.1045 +    }
  1.1046 +
  1.1047 +    return profile;
  1.1048 +}
  1.1049 +
  1.1050 +//-----------------------------------------------------------------------------
  1.1051 +Profile* ProfileManager::GetProfile(const ProfileDeviceKey& deviceKey, const char* user)
  1.1052 +{
  1.1053 +    Lock::Locker lockScope(&ProfileLock);
  1.1054 +
  1.1055 +    if (ProfileCache == NULL)
  1.1056 +    {   // Load the cache
  1.1057 +        LoadCache(false);
  1.1058 +        if (ProfileCache == NULL)
  1.1059 +            return NULL;
  1.1060 +    }
  1.1061 +    
  1.1062 +    Profile* profile = new Profile(BasePath);
  1.1063 +
  1.1064 +    if (deviceKey.Valid)
  1.1065 +    {
  1.1066 +        if (!profile->LoadDeviceProfile(deviceKey) && (user == NULL))
  1.1067 +        {
  1.1068 +            profile->Release();
  1.1069 +            return NULL;
  1.1070 +        }
  1.1071 +    }
  1.1072 +    
  1.1073 +    if (user)
  1.1074 +    {
  1.1075 +        const char* product_str = deviceKey.ProductName.IsEmpty() ? NULL : deviceKey.ProductName.ToCStr();
  1.1076 +        const char* serial_str = deviceKey.PrintedSerial.IsEmpty() ? NULL : deviceKey.PrintedSerial.ToCStr();
  1.1077 +
  1.1078 +        if (!profile->LoadProfile(ProfileCache.GetPtr(), user, product_str, serial_str))
  1.1079 +        {
  1.1080 +            profile->Release();
  1.1081 +            return NULL;
  1.1082 +        }
  1.1083 +    }
  1.1084 +
  1.1085 +    return profile;
  1.1086 +}
  1.1087 +
  1.1088 +
  1.1089 +//-----------------------------------------------------------------------------
  1.1090 +// ***** Profile
  1.1091 +
  1.1092 +Profile::~Profile()
  1.1093 +{
  1.1094 +    ValMap.Clear();
  1.1095 +    for (unsigned int i=0; i<Values.GetSize(); i++)
  1.1096 +        Values[i]->Release();
  1.1097 +
  1.1098 +    Values.Clear();
  1.1099 +}
  1.1100 +
  1.1101 +bool Profile::Close()
  1.1102 +{
  1.1103 +    // TODO:
  1.1104 +    return true;
  1.1105 +}
  1.1106 +
  1.1107 +//-----------------------------------------------------------------------------
  1.1108 +void Profile::CopyItems(JSON* root, String prefix)
  1.1109 +{
  1.1110 +    JSON* item = root->GetFirstItem();
  1.1111 +    while (item)
  1.1112 +    {
  1.1113 +        String item_name;
  1.1114 +        if (prefix.IsEmpty())
  1.1115 +            item_name = item->Name;
  1.1116 +        else
  1.1117 +            item_name = prefix + "." + item->Name;
  1.1118 +
  1.1119 +        if (item->Type == JSON_Object)
  1.1120 +        {   // recursively copy the children
  1.1121 +            
  1.1122 +            CopyItems(item, item_name);
  1.1123 +        }
  1.1124 +        else
  1.1125 +        {
  1.1126 +            //Settings.Set(item_name, item->Value);
  1.1127 +            SetValue(item);
  1.1128 +        }
  1.1129 +
  1.1130 +        item = root->GetNextItem(item);
  1.1131 +    }
  1.1132 +}
  1.1133 +
  1.1134 +//-----------------------------------------------------------------------------
  1.1135 +bool Profile::LoadDeviceFile(unsigned int productId, const char* printedSerialNumber)
  1.1136 +{
  1.1137 +    if (printedSerialNumber[0] == 0)
  1.1138 +        return false;
  1.1139 +
  1.1140 +    String path = BasePath + "/Devices.json";
  1.1141 +
  1.1142 +    // Load the device profiles
  1.1143 +    Ptr<JSON> root = *JSON::Load(path);
  1.1144 +    if (root == NULL)
  1.1145 +        return false;
  1.1146 +
  1.1147 +    // Quick sanity check of the file type and format before we parse it
  1.1148 +    JSON* version = root->GetFirstItem();
  1.1149 +    if (version && version->Name == "Oculus Device Profile Version")
  1.1150 +    {   
  1.1151 +        int major = atoi(version->Value.ToCStr());
  1.1152 +        if (major > MAX_DEVICE_PROFILE_MAJOR_VERSION)
  1.1153 +            return false;   // don't parse the file on unsupported major version number
  1.1154 +    }
  1.1155 +    else
  1.1156 +    {
  1.1157 +        return false;
  1.1158 +    }   
  1.1159 +
  1.1160 +    JSON* device = root->GetNextItem(version);
  1.1161 +    while (device)
  1.1162 +    {   
  1.1163 +        if (device->Name == "Device")
  1.1164 +        {   
  1.1165 +            JSON* product_item = device->GetItemByName("ProductID");
  1.1166 +            JSON* serial_item = device->GetItemByName("Serial");
  1.1167 +            if (product_item && serial_item &&
  1.1168 +                (product_item->dValue == productId) && (serial_item->Value == printedSerialNumber))
  1.1169 +            {   
  1.1170 +                // found the entry for this device so recursively copy all the settings to the profile
  1.1171 +                CopyItems(device, "");
  1.1172 +                return true;   
  1.1173 +            }
  1.1174 +        }
  1.1175 +
  1.1176 +        device = root->GetNextItem(device);
  1.1177 +    }
  1.1178 +    
  1.1179 +    return false;
  1.1180 +}
  1.1181 +
  1.1182 +#if 0
  1.1183 +//-----------------------------------------------------------------------------
  1.1184 +static int BCDByte(unsigned int byte)
  1.1185 +{
  1.1186 +    int digit1 = (byte >> 4) & 0x000f;
  1.1187 +    int digit2 = byte & 0x000f;
  1.1188 +    int decimal = digit1 * 10 + digit2;
  1.1189 +    return decimal;
  1.1190 +}
  1.1191 +#endif
  1.1192 +
  1.1193 +//-----------------------------------------------------------------------------
  1.1194 +bool Profile::LoadDeviceProfile(const ProfileDeviceKey& deviceKey)
  1.1195 +{
  1.1196 +    bool success = false;
  1.1197 +    if (!deviceKey.Valid)
  1.1198 +            return false;
  1.1199 +
  1.1200 +#if 0
  1.1201 +        int dev_major = BCDByte((sinfo.Version >> 8) & 0x00ff);
  1.1202 +        OVR_UNUSED(dev_major);
  1.1203 +        //int dev_minor = BCDByte(sinfo.Version & 0xff);
  1.1204 +      
  1.1205 +        //if (dev_minor > 18)
  1.1206 +        //{   // If the firmware supports hardware stored profiles then grab the device profile
  1.1207 +            // from the sensor
  1.1208 +            // TBD:  Implement this
  1.1209 +        //}
  1.1210 +        //else
  1.1211 +        {
  1.1212 +#endif
  1.1213 +            // Grab the model and serial number from the device and use it to access the device
  1.1214 +            // profile file stored on the local machine
  1.1215 +        success = LoadDeviceFile(deviceKey.ProductId, deviceKey.PrintedSerial);
  1.1216 +    //}
  1.1217 +
  1.1218 +    return success;
  1.1219 +}
  1.1220 +
  1.1221 +//-----------------------------------------------------------------------------
  1.1222 +bool Profile::LoadUser(JSON* root, 
  1.1223 +                         const char* user,
  1.1224 +                          const char* model_name,
  1.1225 +                          const char* device_serial)
  1.1226 +{
  1.1227 +    if (user == NULL)
  1.1228 +        return false;
  1.1229 +
  1.1230 +    // For legacy files, convert to old style names
  1.1231 +    //if (model_name && OVR_strcmp(model_name, "Oculus Rift DK1") == 0)
  1.1232 +    //    model_name = "RiftDK1";
  1.1233 +    
  1.1234 +    bool user_found = false;
  1.1235 +    JSON* data = root->GetItemByName("TaggedData");
  1.1236 +    if (data)
  1.1237 +    {   
  1.1238 +        const char* tag_names[3];
  1.1239 +        const char* tags[3];
  1.1240 +        tag_names[0] = "User";
  1.1241 +        tags[0] = user;
  1.1242 +        int num_tags = 1;
  1.1243 +
  1.1244 +        if (model_name)
  1.1245 +        {
  1.1246 +            tag_names[num_tags] = "Product";
  1.1247 +            tags[num_tags] = model_name;
  1.1248 +            num_tags++;
  1.1249 +        }
  1.1250 +
  1.1251 +        if (device_serial)
  1.1252 +        {
  1.1253 +            tag_names[num_tags] = "Serial";
  1.1254 +            tags[num_tags] = device_serial;
  1.1255 +            num_tags++;
  1.1256 +        }
  1.1257 +
  1.1258 +        // Retrieve all tag permutations
  1.1259 +        for (int combos=1; combos<=num_tags; combos++)
  1.1260 +        {
  1.1261 +            for (int i=0; i<(num_tags - combos + 1); i++)
  1.1262 +            {
  1.1263 +                JSON* vals = FindTaggedData(data, tag_names+i, tags+i, combos);
  1.1264 +                if (vals)
  1.1265 +                {   
  1.1266 +                    if (i==0)   // This tag-combination contains a user match
  1.1267 +                        user_found = true;
  1.1268 +
  1.1269 +                    // Add the values to the Profile.  More specialized multi-tag values
  1.1270 +                    // will take precedence over and overwrite generalized ones 
  1.1271 +                    // For example: ("Me","RiftDK1").IPD would overwrite ("Me").IPD
  1.1272 +                    JSON* item = vals->GetFirstItem();
  1.1273 +                    while (item)
  1.1274 +                    {
  1.1275 +                        //printf("Add %s, %s\n", item->Name.ToCStr(), item->Value.ToCStr());
  1.1276 +                        //Settings.Set(item->Name, item->Value);
  1.1277 +                        SetValue(item);
  1.1278 +                        item = vals->GetNextItem(item);
  1.1279 +                    }
  1.1280 +                }
  1.1281 +            }
  1.1282 +        }
  1.1283 +    }
  1.1284 +
  1.1285 +    if (user_found)
  1.1286 +        SetValue(OVR_KEY_USER, user);
  1.1287 +
  1.1288 +    return user_found;
  1.1289 +}
  1.1290 +
  1.1291 +
  1.1292 +//-----------------------------------------------------------------------------
  1.1293 +bool Profile::LoadProfile(JSON* root,
  1.1294 +                          const char* user,
  1.1295 +                          const char* device_model,
  1.1296 +                          const char* device_serial)
  1.1297 +{
  1.1298 +    if (!LoadUser(root, user, device_model, device_serial))
  1.1299 +        return false;
  1.1300 +
  1.1301 +    return true;
  1.1302 +}
  1.1303 +
  1.1304 +
  1.1305 +//-----------------------------------------------------------------------------
  1.1306 +char* Profile::GetValue(const char* key, char* val, int val_length) const
  1.1307 +{
  1.1308 +    JSON* value = NULL;
  1.1309 +    if (ValMap.Get(key, &value))
  1.1310 +    {
  1.1311 +        OVR_strcpy(val, val_length, value->Value.ToCStr());
  1.1312 +        return val;
  1.1313 +    }
  1.1314 +    else
  1.1315 +    {
  1.1316 +        val[0] = 0;
  1.1317 +        return NULL;
  1.1318 +    }
  1.1319 +}
  1.1320 +
  1.1321 +//-----------------------------------------------------------------------------
  1.1322 +const char* Profile::GetValue(const char* key)
  1.1323 +{
  1.1324 +    // Non-reentrant query.  The returned buffer can only be used until the next call
  1.1325 +    // to GetValue()
  1.1326 +    JSON* value = NULL;
  1.1327 +    if (ValMap.Get(key, &value))
  1.1328 +    {
  1.1329 +        TempVal = value->Value;
  1.1330 +        return TempVal.ToCStr();
  1.1331 +    }
  1.1332 +    else
  1.1333 +    {
  1.1334 +        return NULL;
  1.1335 +    }
  1.1336 +}
  1.1337 +
  1.1338 +//-----------------------------------------------------------------------------
  1.1339 +int Profile::GetNumValues(const char* key) const
  1.1340 +{
  1.1341 +    JSON* value = NULL;
  1.1342 +    if (ValMap.Get(key, &value))
  1.1343 +    {  
  1.1344 +        if (value->Type == JSON_Array)
  1.1345 +            return value->GetArraySize();
  1.1346 +        else
  1.1347 +            return 1;
  1.1348 +    }
  1.1349 +    else
  1.1350 +        return 0;        
  1.1351 +}
  1.1352 +
  1.1353 +//-----------------------------------------------------------------------------
  1.1354 +bool Profile::GetBoolValue(const char* key, bool default_val) const
  1.1355 +{
  1.1356 +    JSON* value = NULL;
  1.1357 +    if (ValMap.Get(key, &value) && value->Type == JSON_Bool)
  1.1358 +        return (value->dValue != 0);
  1.1359 +    else
  1.1360 +        return default_val;
  1.1361 +}
  1.1362 +
  1.1363 +//-----------------------------------------------------------------------------
  1.1364 +int Profile::GetIntValue(const char* key, int default_val) const
  1.1365 +{
  1.1366 +    JSON* value = NULL;
  1.1367 +    if (ValMap.Get(key, &value) && value->Type == JSON_Number)
  1.1368 +        return (int)(value->dValue);
  1.1369 +    else
  1.1370 +        return default_val;
  1.1371 +}
  1.1372 +
  1.1373 +//-----------------------------------------------------------------------------
  1.1374 +float Profile::GetFloatValue(const char* key, float default_val) const
  1.1375 +{
  1.1376 +    JSON* value = NULL;
  1.1377 +    if (ValMap.Get(key, &value) && value->Type == JSON_Number)
  1.1378 +        return (float)(value->dValue);
  1.1379 +    else
  1.1380 +        return default_val;
  1.1381 +}
  1.1382 +
  1.1383 +//-----------------------------------------------------------------------------
  1.1384 +int Profile::GetFloatValues(const char* key, float* values, int num_vals) const
  1.1385 +{
  1.1386 +    JSON* value = NULL;
  1.1387 +    if (ValMap.Get(key, &value) && value->Type == JSON_Array)
  1.1388 +    {
  1.1389 +        int val_count = Alg::Min(value->GetArraySize(), num_vals);
  1.1390 +        JSON* item = value->GetFirstItem();
  1.1391 +        int count=0;
  1.1392 +        while (item && count < val_count)
  1.1393 +        {
  1.1394 +            if (item->Type == JSON_Number)
  1.1395 +                values[count] = (float)item->dValue;
  1.1396 +            else
  1.1397 +                break;
  1.1398 +
  1.1399 +            count++;
  1.1400 +            item = value->GetNextItem(item);
  1.1401 +        }
  1.1402 +
  1.1403 +        return count;
  1.1404 +    }
  1.1405 +    else
  1.1406 +    {
  1.1407 +        return 0;
  1.1408 +    }
  1.1409 +}
  1.1410 +
  1.1411 +//-----------------------------------------------------------------------------
  1.1412 +double Profile::GetDoubleValue(const char* key, double default_val) const
  1.1413 +{
  1.1414 +    JSON* value = NULL;
  1.1415 +    if (ValMap.Get(key, &value) && value->Type == JSON_Number)
  1.1416 +        return value->dValue;
  1.1417 +    else
  1.1418 +        return default_val;
  1.1419 +}
  1.1420 +
  1.1421 +//-----------------------------------------------------------------------------
  1.1422 +int Profile::GetDoubleValues(const char* key, double* values, int num_vals) const
  1.1423 +{
  1.1424 +    JSON* value = NULL;
  1.1425 +    if (ValMap.Get(key, &value) && value->Type == JSON_Array)
  1.1426 +    {
  1.1427 +        int val_count = Alg::Min(value->GetArraySize(), num_vals);
  1.1428 +        JSON* item = value->GetFirstItem();
  1.1429 +        int count=0;
  1.1430 +        while (item && count < val_count)
  1.1431 +        {
  1.1432 +            if (item->Type == JSON_Number)
  1.1433 +                values[count] = item->dValue;
  1.1434 +            else
  1.1435 +                break;
  1.1436 +
  1.1437 +            count++;
  1.1438 +            item = value->GetNextItem(item);
  1.1439 +        }
  1.1440 +
  1.1441 +        return count;
  1.1442 +    }
  1.1443 +    return 0;
  1.1444 +}
  1.1445 +
  1.1446 +//-----------------------------------------------------------------------------
  1.1447 +void Profile::SetValue(JSON* val)
  1.1448 +{
  1.1449 +    if (val == NULL)
  1.1450 +        return;
  1.1451 +
  1.1452 +    if (val->Type == JSON_Number)
  1.1453 +        SetDoubleValue(val->Name, val->dValue);
  1.1454 +    else if (val->Type == JSON_Bool)
  1.1455 +        SetBoolValue(val->Name, (val->dValue != 0));
  1.1456 +    else if (val->Type == JSON_String)
  1.1457 +        SetValue(val->Name, val->Value);
  1.1458 +    else if (val->Type == JSON_Array)
  1.1459 +    {
  1.1460 +        // Create a copy of the array
  1.1461 +        JSON* value = val->Copy();
  1.1462 +        Values.PushBack(value);
  1.1463 +        ValMap.Set(value->Name, value);
  1.1464 +    }
  1.1465 +}
  1.1466 +
  1.1467 +//-----------------------------------------------------------------------------
  1.1468 +void Profile::SetValue(const char* key, const char* val)
  1.1469 +{
  1.1470 +    if (key == NULL || val == NULL)
  1.1471 +        return;
  1.1472 +
  1.1473 +    JSON* value = NULL;
  1.1474 +    if (ValMap.Get(key, &value))
  1.1475 +    {
  1.1476 +        value->Value = val;
  1.1477 +    }
  1.1478 +    else
  1.1479 +    {
  1.1480 +        value = JSON::CreateString(val);
  1.1481 +        value->Name = key;
  1.1482 +
  1.1483 +        Values.PushBack(value);
  1.1484 +        ValMap.Set(key, value);
  1.1485 +    }
  1.1486 +}
  1.1487 +
  1.1488 +//-----------------------------------------------------------------------------
  1.1489 +void Profile::SetBoolValue(const char* key, bool val)
  1.1490 +{
  1.1491 +    if (key == NULL)
  1.1492 +        return;
  1.1493 +
  1.1494 +    JSON* value = NULL;
  1.1495 +    if (ValMap.Get(key, &value))
  1.1496 +    {
  1.1497 +        value->dValue = val;
  1.1498 +    }
  1.1499 +    else
  1.1500 +    {
  1.1501 +        value = JSON::CreateBool(val);
  1.1502 +        value->Name = key;
  1.1503 +
  1.1504 +        Values.PushBack(value);
  1.1505 +        ValMap.Set(key, value);
  1.1506 +    }
  1.1507 +}
  1.1508 +
  1.1509 +//-----------------------------------------------------------------------------
  1.1510 +void Profile::SetIntValue(const char* key, int val)
  1.1511 +{
  1.1512 +    SetDoubleValue(key, val);
  1.1513 +}
  1.1514 +
  1.1515 +//-----------------------------------------------------------------------------
  1.1516 +void Profile::SetFloatValue(const char* key, float val)
  1.1517 +{
  1.1518 +    SetDoubleValue(key, val);
  1.1519 +}
  1.1520 +
  1.1521 +//-----------------------------------------------------------------------------
  1.1522 +void Profile::SetFloatValues(const char* key, const float* vals, int num_vals)
  1.1523 +{
  1.1524 +    JSON* value = NULL;
  1.1525 +    int val_count = 0;
  1.1526 +    if (ValMap.Get(key, &value))
  1.1527 +    {
  1.1528 +        if (value->Type == JSON_Array)
  1.1529 +        {
  1.1530 +            // truncate the existing array if fewer entries provided
  1.1531 +            int num_existing_vals = value->GetArraySize();
  1.1532 +            for (int i=num_vals; i<num_existing_vals; i++)
  1.1533 +                value->RemoveLast();
  1.1534 +            
  1.1535 +            JSON* item = value->GetFirstItem();
  1.1536 +            while (item && val_count < num_vals)
  1.1537 +            {
  1.1538 +                if (item->Type == JSON_Number)
  1.1539 +                    item->dValue = vals[val_count];
  1.1540 +
  1.1541 +                item = value->GetNextItem(item);
  1.1542 +                val_count++;
  1.1543 +            }
  1.1544 +        }
  1.1545 +        else
  1.1546 +        {
  1.1547 +            return;  // Maybe we should change the data type?
  1.1548 +        }
  1.1549 +    }
  1.1550 +    else
  1.1551 +    {
  1.1552 +        value = JSON::CreateArray();
  1.1553 +        value->Name = key;
  1.1554 +
  1.1555 +        Values.PushBack(value);
  1.1556 +        ValMap.Set(key, value);
  1.1557 +    }
  1.1558 +
  1.1559 +    for (; val_count < num_vals; val_count++)
  1.1560 +        value->AddArrayNumber(vals[val_count]);
  1.1561 +}
  1.1562 +
  1.1563 +//-----------------------------------------------------------------------------
  1.1564 +void Profile::SetDoubleValue(const char* key, double val)
  1.1565 +{
  1.1566 +    JSON* value = NULL;
  1.1567 +    if (ValMap.Get(key, &value))
  1.1568 +    {
  1.1569 +        value->dValue = val;
  1.1570 +    }
  1.1571 +    else
  1.1572 +    {
  1.1573 +        value = JSON::CreateNumber(val);
  1.1574 +        value->Name = key;
  1.1575 +
  1.1576 +        Values.PushBack(value);
  1.1577 +        ValMap.Set(key, value);
  1.1578 +    }
  1.1579 +}
  1.1580 +
  1.1581 +//-----------------------------------------------------------------------------
  1.1582 +void Profile::SetDoubleValues(const char* key, const double* vals, int num_vals)
  1.1583 +{
  1.1584 +    JSON* value = NULL;
  1.1585 +    int val_count = 0;
  1.1586 +    if (ValMap.Get(key, &value))
  1.1587 +    {
  1.1588 +        if (value->Type == JSON_Array)
  1.1589 +        {
  1.1590 +            // truncate the existing array if fewer entries provided
  1.1591 +            int num_existing_vals = value->GetArraySize();
  1.1592 +            for (int i=num_vals; i<num_existing_vals; i++)
  1.1593 +                value->RemoveLast();
  1.1594 +            
  1.1595 +            JSON* item = value->GetFirstItem();
  1.1596 +            while (item && val_count < num_vals)
  1.1597 +            {
  1.1598 +                if (item->Type == JSON_Number)
  1.1599 +                    item->dValue = vals[val_count];
  1.1600 +
  1.1601 +                item = value->GetNextItem(item);
  1.1602 +                val_count++;
  1.1603 +            }
  1.1604 +        }
  1.1605 +        else
  1.1606 +        {
  1.1607 +            return;  // Maybe we should change the data type?
  1.1608 +        }
  1.1609 +    }
  1.1610 +    else
  1.1611 +    {
  1.1612 +        value = JSON::CreateArray();
  1.1613 +        value->Name = key;
  1.1614 +
  1.1615 +        Values.PushBack(value);
  1.1616 +        ValMap.Set(key, value);
  1.1617 +    }
  1.1618 +
  1.1619 +    for (; val_count < num_vals; val_count++)
  1.1620 +        value->AddArrayNumber(vals[val_count]);
  1.1621 +}
  1.1622 +
  1.1623 +//------------------------------------------------------------------------------
  1.1624 +bool Profile::IsDefaultProfile()
  1.1625 +{
  1.1626 +    return 0 == OVR::String::CompareNoCase("Default", GetValue(OVR_KEY_NAME));
  1.1627 +}
  1.1628 +
  1.1629 +
  1.1630 +}  // namespace OVR