OVR_Profile.cpp
Go to the documentation of this file.
00001 /************************************************************************************
00002 
00003 PublicHeader:   None
00004 Filename    :   OVR_Profile.cpp
00005 Content     :   Structs and functions for loading and storing device profile settings
00006 Created     :   February 14, 2013
00007 Notes       :
00008    
00009    Profiles are used to store per-user settings that can be transferred and used
00010    across multiple applications.  For example, player IPD can be configured once 
00011    and reused for a unified experience across games.  Configuration and saving of profiles
00012    can be accomplished in game via the Profile API or by the official Oculus Configuration
00013    Utility.
00014 
00015 Copyright   :   Copyright 2013 Oculus VR, Inc. All Rights reserved.
00016 
00017 Use of this software is subject to the terms of the Oculus license
00018 agreement provided at the time of installation or download, or which
00019 otherwise accompanies this software in either electronic or hard copy form.
00020 
00021 ************************************************************************************/
00022 
00023 #include "OVR_Profile.h"
00024 #include "OVR_JSON.h"
00025 #include "Kernel/OVR_Types.h"
00026 #include "Kernel/OVR_SysFile.h"
00027 #include "Kernel/OVR_Allocator.h"
00028 #include "Kernel/OVR_Array.h"
00029 
00030 #ifdef OVR_OS_WIN32
00031 #include <Shlobj.h>
00032 #else
00033 #include <dirent.h>
00034 #include <sys/stat.h>
00035 
00036 #ifdef OVR_OS_LINUX
00037 #include <unistd.h>
00038 #include <pwd.h>
00039 #endif
00040 
00041 #endif
00042 
00043 #define PROFILE_VERSION 1.0
00044 
00045 namespace OVR {
00046 
00047 //-----------------------------------------------------------------------------
00048 // Returns the pathname of the JSON file containing the stored profiles
00049 String GetProfilePath(bool create_dir)
00050 {
00051     String path;
00052 
00053 #if defined(OVR_OS_WIN32)
00054     
00055     PWSTR data_path = NULL;
00056     SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &data_path);
00057     path = String(data_path);
00058     CoTaskMemFree(data_path);
00059     
00060     path += "/Oculus";
00061 
00062     if (create_dir)
00063     {   // Create the Oculus directory if it doesn't exist
00064         WCHAR wpath[128];
00065         OVR::UTF8Util::DecodeString(wpath, path.ToCStr());
00066 
00067         DWORD attrib = GetFileAttributes(wpath);
00068         bool exists = attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY);
00069         if (!exists)
00070         {   
00071             CreateDirectory(wpath, NULL);
00072         }
00073     }
00074         
00075 #elif defined(OVR_OS_MAC)
00076 
00077     const char* home = getenv("HOME");
00078     path = home;
00079     path += "/Library/Preferences/Oculus";
00080 
00081     if (create_dir)
00082     {   // Create the Oculus directory if it doesn't exist
00083         DIR* dir = opendir(path);
00084         if (dir == NULL)
00085         {
00086             mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
00087         }
00088         else
00089         {
00090             closedir(dir);
00091         }
00092     }
00093 
00094 #else
00095 
00096     passwd* pwd = getpwuid(getuid());
00097     const char* home = pwd->pw_dir;
00098     path = home;
00099     path += "/.config/Oculus";
00100 
00101     if (create_dir)
00102     {   // Create the Oculus directory if it doesn't exist
00103         DIR* dir = opendir(path);
00104         if (dir == NULL)
00105         {
00106             mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
00107         }
00108         else
00109         {
00110             closedir(dir);
00111         }
00112     }
00113 
00114 #endif
00115 
00116     path += "/Profiles.json";
00117     return path;
00118 }
00119 
00120 
00121 //-----------------------------------------------------------------------------
00122 // ***** ProfileManager
00123 
00124 ProfileManager::ProfileManager()
00125 {
00126     Changed = false;
00127     CacheDevice = Profile_Unknown;
00128 }
00129 
00130 ProfileManager::~ProfileManager()
00131 {
00132     // If the profiles have been altered then write out the profile file
00133     if (Changed)
00134         SaveCache();
00135 
00136     ClearCache();
00137 }
00138 
00139 ProfileManager* ProfileManager::Create()
00140 {
00141     return new ProfileManager();
00142 }
00143 
00144 Profile* ProfileManager::CreateProfileObject(const char* user,
00145                                              ProfileType device,
00146                                              const char** device_name)
00147 {
00148     Lock::Locker lockScope(&ProfileLock);
00149 
00150     Profile* profile = NULL;
00151     switch (device)
00152     {
00153         case Profile_RiftDK1:
00154             *device_name = "RiftDK1";
00155             profile = new RiftDK1Profile(user);
00156             break;
00157         case Profile_RiftDKHD:
00158         case Profile_Unknown:
00159             break;
00160     }
00161 
00162     return profile;
00163 }
00164 
00165 
00166 // Clear the local profile cache
00167 void ProfileManager::ClearCache()
00168 {
00169     Lock::Locker lockScope(&ProfileLock);
00170 
00171     ProfileCache.Clear();
00172     CacheDevice = Profile_Unknown;
00173 }
00174 
00175 
00176 // Poplulates the local profile cache.  This occurs on the first access of the profile
00177 // data.  All profile operations are performed against the local cache until the
00178 // ProfileManager is released or goes out of scope at which time the cache is serialized
00179 // to disk.
00180 void ProfileManager::LoadCache(ProfileType device)
00181 {
00182     Lock::Locker lockScope(&ProfileLock);
00183 
00184     ClearCache();
00185 
00186     String path = GetProfilePath(false);
00187 
00188     Ptr<JSON> root = *JSON::Load(path);
00189     if (!root || root->GetItemCount() < 3)
00190         return;
00191 
00192     // First read the file type and version to make sure this is a valid file
00193     JSON* item0 = root->GetFirstItem();
00194     JSON* item1 = root->GetNextItem(item0);
00195     JSON* item2 = root->GetNextItem(item1);
00196 
00197     if (OVR_strcmp(item0->Name, "Oculus Profile Version") == 0)
00198     {   // In the future I may need to check versioning to determine parse method
00199     }
00200     else
00201     {
00202         return;
00203     }
00204 
00205     DefaultProfile = item1->Value;
00206 
00207     // Read the number of profiles
00208     int   profileCount = (int)item2->dValue;
00209     JSON* profileItem  = item2;
00210 
00211     for (int p=0; p<profileCount; p++)
00212     {
00213         profileItem = profileItem->GetNextItem(profileItem);
00214         if (!profileItem)
00215             break;
00216         
00217         // Read the required Name field
00218         const char* profileName;
00219         JSON* item = profileItem->GetFirstItem();
00220         
00221         if (item && (OVR_strcmp(item->Name, "Name") == 0))
00222         {   
00223             profileName = item->Value;
00224         }
00225         else
00226         {
00227             return;   // invalid field
00228         }
00229 
00230         const char*   deviceName  = 0;
00231         bool          deviceFound = false;
00232         Ptr<Profile>  profile     = *CreateProfileObject(profileName, device, &deviceName);
00233 
00234         // Read the base profile fields.
00235         while (item = profileItem->GetNextItem(item), item)
00236         {
00237             if (item->Type != JSON_Object)
00238             {
00239                 profile->ParseProperty(item->Name, item->Value);
00240             }
00241             else
00242             {   // Search for the matching device to get device specific fields
00243                 if (!deviceFound && OVR_strcmp(item->Name, deviceName) == 0)
00244                 {
00245                     deviceFound = true;
00246 
00247                     for (JSON* deviceItem = item->GetFirstItem(); deviceItem;
00248                          deviceItem = item->GetNextItem(deviceItem))
00249                     {
00250                         profile->ParseProperty(deviceItem->Name, deviceItem->Value);
00251                     }
00252                 }
00253             }
00254         }
00255 
00256         // Add the new profile
00257         if (deviceFound)
00258             ProfileCache.PushBack(profile);
00259     }
00260 
00261     CacheDevice = device;
00262 }
00263 
00264 
00265 // Serializes the profiles to disk.
00266 void ProfileManager::SaveCache()
00267 {
00268     String path = GetProfilePath(true);
00269  
00270     Lock::Locker lockScope(&ProfileLock);
00271 
00272     // TODO: Since there is only a single device type now, a full tree overwrite
00273     // is sufficient but in the future a selective device branch replacement will
00274     // be necessary
00275 
00276     Ptr<JSON> root = *JSON::CreateObject();
00277     root->AddNumberItem("Oculus Profile Version", PROFILE_VERSION);
00278     root->AddStringItem("CurrentProfile", DefaultProfile);
00279     root->AddNumberItem("ProfileCount", (double) ProfileCache.GetSize());
00280 
00281     // Generate a JSON subtree for each profile
00282     for (unsigned int i=0; i<ProfileCache.GetSize(); i++)
00283     {
00284         Profile* profile = ProfileCache[i];
00285 
00286         JSON* json_profile = JSON::CreateObject();
00287         json_profile->Name = "Profile";
00288         json_profile->AddStringItem("Name", profile->Name);
00289         const char* gender;
00290         switch (profile->GetGender())
00291         {
00292             case Profile::Gender_Male:   gender = "Male"; break;
00293             case Profile::Gender_Female: gender = "Female"; break;
00294             default: gender = "Unspecified";
00295         }
00296         json_profile->AddStringItem("Gender", gender);
00297         json_profile->AddNumberItem("PlayerHeight", profile->PlayerHeight);
00298         json_profile->AddNumberItem("IPD", profile->IPD);
00299 
00300         if (profile->Type == Profile_RiftDK1)
00301         {
00302             RiftDK1Profile* riftdk1 = (RiftDK1Profile*)profile;
00303             JSON* json_riftdk1 = JSON::CreateObject();
00304             json_profile->AddItem("RiftDK1", json_riftdk1);
00305 
00306             const char* eyecup = "A";
00307             switch (riftdk1->EyeCups)
00308             {
00309                 case RiftDK1Profile::EyeCup_A: eyecup = "A"; break;
00310                 case RiftDK1Profile::EyeCup_B: eyecup = "B"; break;
00311                 case RiftDK1Profile::EyeCup_C: eyecup = "C"; break;
00312             }
00313             json_riftdk1->AddStringItem("EyeCup", eyecup);
00314             json_riftdk1->AddNumberItem("LL", riftdk1->LL);
00315             json_riftdk1->AddNumberItem("LR", riftdk1->LR);
00316             json_riftdk1->AddNumberItem("RL", riftdk1->RL);
00317             json_riftdk1->AddNumberItem("RR", riftdk1->RR);
00318         }
00319 
00320         root->AddItem("Profile", json_profile);
00321     }
00322 
00323     root->Save(path);
00324 }
00325 
00326 // Returns the number of stored profiles for this device type
00327 int ProfileManager::GetProfileCount(ProfileType device)
00328 {
00329     Lock::Locker lockScope(&ProfileLock);
00330 
00331     if (CacheDevice == Profile_Unknown)
00332         LoadCache(device);
00333 
00334     return (int)ProfileCache.GetSize();
00335 }
00336 
00337 // Returns the profile name of a specific profile in the list.  The returned 
00338 // memory is locally allocated and should not be stored or deleted.  Returns NULL
00339 // if the index is invalid
00340 const char* ProfileManager::GetProfileName(ProfileType device, unsigned int index)
00341 {
00342     Lock::Locker lockScope(&ProfileLock);
00343 
00344     if (CacheDevice == Profile_Unknown)
00345         LoadCache(device);
00346 
00347     if (index < ProfileCache.GetSize())
00348     {
00349         Profile* profile = ProfileCache[index];
00350         OVR_strcpy(NameBuff, Profile::MaxNameLen, profile->Name);
00351         return NameBuff;
00352     }
00353     else
00354     {
00355         return NULL;
00356     }
00357 }
00358 
00359 bool ProfileManager::HasProfile(ProfileType device, const char* name)
00360 {
00361     Lock::Locker lockScope(&ProfileLock);
00362 
00363     if (CacheDevice == Profile_Unknown)
00364         LoadCache(device);
00365 
00366     for (unsigned i = 0; i< ProfileCache.GetSize(); i++)
00367     {
00368         if (ProfileCache[i] && OVR_strcmp(ProfileCache[i]->Name, name) == 0)
00369             return true;
00370     }
00371     return false;
00372 }
00373 
00374 
00375 // Returns a specific profile object in the list.  The returned memory should be
00376 // encapsulated in a Ptr<> object or released after use.  Returns NULL if the index
00377 // is invalid
00378 Profile* ProfileManager::LoadProfile(ProfileType device, unsigned int index)
00379 {
00380     Lock::Locker lockScope(&ProfileLock);
00381 
00382     if (CacheDevice == Profile_Unknown)
00383         LoadCache(device);
00384 
00385     if (index < ProfileCache.GetSize())
00386     {
00387         Profile* profile = ProfileCache[index];
00388         return profile->Clone();
00389     }
00390     else
00391     {
00392         return NULL;
00393     }
00394 }
00395 
00396 // Returns a profile object for a particular device and user name.  The returned 
00397 // memory should be encapsulated in a Ptr<> object or released after use.  Returns 
00398 // NULL if the profile is not found
00399 Profile* ProfileManager::LoadProfile(ProfileType device, const char* user)
00400 {
00401     if (user == NULL)
00402         return NULL;
00403 
00404     Lock::Locker lockScope(&ProfileLock);
00405     
00406     if (CacheDevice == Profile_Unknown)
00407         LoadCache(device);
00408 
00409     for (unsigned int i=0; i<ProfileCache.GetSize(); i++)
00410     {
00411         if (OVR_strcmp(user, ProfileCache[i]->Name) == 0)
00412         {   // Found the requested user profile
00413             Profile* profile = ProfileCache[i];
00414             return profile->Clone();
00415         }
00416     }
00417 
00418     return NULL;
00419 }
00420 
00421 // Returns a profile with all system default values
00422 Profile* ProfileManager::GetDeviceDefaultProfile(ProfileType device)
00423 {
00424     const char* device_name = NULL;
00425     return CreateProfileObject("default", device, &device_name);
00426 }
00427 
00428 // Returns the name of the profile that is marked as the current default user.
00429 const char* ProfileManager::GetDefaultProfileName(ProfileType device)
00430 {
00431     Lock::Locker lockScope(&ProfileLock);
00432 
00433     if (CacheDevice == Profile_Unknown)
00434         LoadCache(device);
00435 
00436     if (ProfileCache.GetSize() > 0)
00437     {
00438         OVR_strcpy(NameBuff, Profile::MaxNameLen, DefaultProfile);
00439         return NameBuff;
00440     }
00441     else
00442     {
00443         return NULL;
00444     }
00445 }
00446 
00447 // Marks a particular user as the current default user.
00448 bool ProfileManager::SetDefaultProfileName(ProfileType device, const char* name)
00449 {
00450     Lock::Locker lockScope(&ProfileLock);
00451 
00452     if (CacheDevice == Profile_Unknown)
00453         LoadCache(device);
00454 // TODO: I should verify that the user is valid
00455     if (ProfileCache.GetSize() > 0)
00456     {
00457         DefaultProfile = name;
00458         Changed = true;
00459         return true;
00460     }
00461     else
00462     {
00463         return false;
00464     }
00465 }
00466 
00467 
00468 // Saves a new or existing profile.  Returns true on success or false on an
00469 // invalid or failed save.
00470 bool ProfileManager::Save(const Profile* profile)
00471 {
00472     Lock::Locker lockScope(&ProfileLock);
00473 
00474     if (OVR_strcmp(profile->Name, "default") == 0)
00475         return false;  // don't save a default profile
00476 
00477     // TODO: I should also verify that this profile type matches the current cache
00478     if (CacheDevice == Profile_Unknown)
00479         LoadCache(profile->Type);
00480 
00481     // Look for the pre-existence of this profile
00482     bool added = false;
00483     for (unsigned int i=0; i<ProfileCache.GetSize(); i++)
00484     {
00485         int compare = OVR_strcmp(profile->Name, ProfileCache[i]->Name);
00486               
00487         if (compare == 0)
00488         {   
00489             // TODO: I should do a proper field comparison to avoid unnecessary
00490             // overwrites and file saves
00491 
00492             // Replace the previous instance with the new profile
00493             ProfileCache[i] = *profile->Clone();
00494             added   = true;
00495             Changed = true;
00496             break;
00497         }
00498     }
00499 
00500     if (!added)
00501     {
00502         ProfileCache.PushBack(*profile->Clone());
00503         if (ProfileCache.GetSize() == 1)
00504             CacheDevice = profile->Type;
00505 
00506         Changed = true;
00507     }
00508 
00509     return true;
00510 }
00511 
00512 // Removes an existing profile.  Returns true if the profile was found and deleted
00513 // and returns false otherwise.
00514 bool ProfileManager::Delete(const Profile* profile)
00515 {
00516     Lock::Locker lockScope(&ProfileLock);
00517 
00518     if (OVR_strcmp(profile->Name, "default") == 0)
00519         return false;  // don't delete a default profile
00520 
00521     if (CacheDevice == Profile_Unknown)
00522         LoadCache(profile->Type);
00523 
00524     // Look for the existence of this profile
00525     for (unsigned int i=0; i<ProfileCache.GetSize(); i++)
00526     {
00527         if (OVR_strcmp(profile->Name, ProfileCache[i]->Name) == 0)
00528         {  
00529             if (OVR_strcmp(profile->Name, DefaultProfile) == 0)
00530                 DefaultProfile.Clear();
00531             
00532             ProfileCache.RemoveAt(i);
00533             Changed = true;
00534             return true;
00535         }
00536     }
00537 
00538     return false;
00539 }
00540 
00541 
00542 
00543 //-----------------------------------------------------------------------------
00544 // ***** Profile
00545 
00546 Profile::Profile(ProfileType device, const char* name)
00547 {
00548     Type         = device;
00549     Gender       = Gender_Unspecified;
00550     PlayerHeight = 1.778f;    // 5'10" inch man
00551     IPD          = 0.064f;
00552     OVR_strcpy(Name, MaxNameLen, name);
00553 }
00554 
00555 
00556 bool Profile::ParseProperty(const char* prop, const char* sval)
00557 {
00558     if (OVR_strcmp(prop, "Name") == 0)
00559     {
00560         OVR_strcpy(Name, MaxNameLen, sval);
00561         return true;
00562     }
00563     else if (OVR_strcmp(prop, "Gender") == 0)
00564     {
00565         if (OVR_strcmp(sval, "Male") == 0)
00566             Gender = Gender_Male;
00567         else if (OVR_strcmp(sval, "Female") == 0)
00568             Gender = Gender_Female;
00569         else
00570             Gender = Gender_Unspecified;
00571 
00572         return true;
00573     }
00574     else if (OVR_strcmp(prop, "PlayerHeight") == 0)
00575     {
00576         PlayerHeight = (float)atof(sval);
00577         return true;
00578     }
00579     else if (OVR_strcmp(prop, "IPD") == 0)
00580     {
00581         IPD = (float)atof(sval);
00582         return true;
00583     }
00584 
00585     return false;
00586 }
00587 
00588 
00589 // Computes the eye height from the metric head height
00590 float Profile::GetEyeHeight()
00591 {
00592     const float EYE_TO_HEADTOP_RATIO =   0.44538f;
00593     const float MALE_AVG_HEAD_HEIGHT =   0.232f;
00594     const float FEMALE_AVG_HEAD_HEIGHT = 0.218f;
00595      
00596     // compute distance from top of skull to the eye
00597     float head_height;
00598     if (Gender == Gender_Female)
00599         head_height = FEMALE_AVG_HEAD_HEIGHT;
00600     else
00601         head_height = MALE_AVG_HEAD_HEIGHT;
00602 
00603     float skull = EYE_TO_HEADTOP_RATIO * head_height;
00604 
00605     float eye_height  = PlayerHeight - skull;
00606     return eye_height;
00607 }
00608 
00609 
00610 //-----------------------------------------------------------------------------
00611 // ***** RiftDK1Profile
00612 
00613 RiftDK1Profile::RiftDK1Profile(const char* name) : Profile(Profile_RiftDK1, name)
00614 {
00615     EyeCups = EyeCup_A;
00616     LL = 0;
00617     LR = 0;
00618     RL = 0;
00619     RR = 0;
00620 }
00621 
00622 bool RiftDK1Profile::ParseProperty(const char* prop, const char* sval)
00623 {
00624     if (OVR_strcmp(prop, "EyeCup") == 0)
00625     {
00626         switch (sval[0])
00627         {
00628             case 'C': EyeCups = EyeCup_C; break;
00629             case 'B': EyeCups = EyeCup_B; break;
00630             default:  EyeCups = EyeCup_A; break;
00631         }
00632         return true;
00633     }
00634     else if (OVR_strcmp(prop, "LL") == 0)
00635     {
00636         LL = atoi(sval);
00637         return true;
00638     }
00639     else if (OVR_strcmp(prop, "LR") == 0)
00640     {
00641         LR = atoi(sval);
00642         return true;
00643     }
00644     else if (OVR_strcmp(prop, "RL") == 0)
00645     {
00646         RL = atoi(sval);
00647         return true;
00648     }
00649     else if (OVR_strcmp(prop, "RR") == 0)
00650     {
00651         RR = atoi(sval);
00652         return true;
00653     }
00654     
00655     return Profile::ParseProperty(prop, sval);
00656 }
00657 
00658 Profile* RiftDK1Profile::Clone() const
00659 {
00660     RiftDK1Profile* profile = new RiftDK1Profile(*this);
00661     return profile;
00662 }
00663 
00664 }  // OVR


oculus_sdk
Author(s):
autogenerated on Mon Oct 6 2014 03:01:19