oculus1
diff libovr/Src/OVR_Profile.cpp @ 1:e2f9e4603129
added LibOVR and started a simple vr wrapper.
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 14 Sep 2013 16:14:59 +0300 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libovr/Src/OVR_Profile.cpp Sat Sep 14 16:14:59 2013 +0300 1.3 @@ -0,0 +1,671 @@ 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 2013 Oculus VR, Inc. All Rights reserved. 1.19 + 1.20 +Use of this software is subject to the terms of the Oculus license 1.21 +agreement provided at the time of installation or download, or which 1.22 +otherwise accompanies this software in either electronic or hard copy form. 1.23 + 1.24 +************************************************************************************/ 1.25 + 1.26 +#include "OVR_Profile.h" 1.27 +#include "OVR_JSON.h" 1.28 +#include "Kernel/OVR_Types.h" 1.29 +#include "Kernel/OVR_SysFile.h" 1.30 +#include "Kernel/OVR_Allocator.h" 1.31 +#include "Kernel/OVR_Array.h" 1.32 + 1.33 +#ifdef OVR_OS_WIN32 1.34 +#include <Shlobj.h> 1.35 +#else 1.36 +#include <dirent.h> 1.37 +#include <sys/stat.h> 1.38 + 1.39 +#ifdef OVR_OS_LINUX 1.40 +#include <unistd.h> 1.41 +#include <pwd.h> 1.42 +#endif 1.43 + 1.44 +#endif 1.45 + 1.46 +#define PROFILE_VERSION 1.0 1.47 + 1.48 +namespace OVR { 1.49 + 1.50 +//----------------------------------------------------------------------------- 1.51 +// Returns the pathname of the JSON file containing the stored profiles 1.52 +String GetBaseOVRPath(bool create_dir) 1.53 +{ 1.54 + String path; 1.55 + 1.56 +#if defined(OVR_OS_WIN32) 1.57 + 1.58 + TCHAR data_path[MAX_PATH]; 1.59 + SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, NULL, 0, data_path); 1.60 + path = String(data_path); 1.61 + 1.62 + path += "/Oculus"; 1.63 + 1.64 + if (create_dir) 1.65 + { // Create the Oculus directory if it doesn't exist 1.66 + WCHAR wpath[128]; 1.67 + OVR::UTF8Util::DecodeString(wpath, path.ToCStr()); 1.68 + 1.69 + DWORD attrib = GetFileAttributes(wpath); 1.70 + bool exists = attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY); 1.71 + if (!exists) 1.72 + { 1.73 + CreateDirectory(wpath, NULL); 1.74 + } 1.75 + } 1.76 + 1.77 +#elif defined(OVR_OS_MAC) 1.78 + 1.79 + const char* home = getenv("HOME"); 1.80 + path = home; 1.81 + path += "/Library/Preferences/Oculus"; 1.82 + 1.83 + if (create_dir) 1.84 + { // Create the Oculus directory if it doesn't exist 1.85 + DIR* dir = opendir(path); 1.86 + if (dir == NULL) 1.87 + { 1.88 + mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO); 1.89 + } 1.90 + else 1.91 + { 1.92 + closedir(dir); 1.93 + } 1.94 + } 1.95 + 1.96 +#else 1.97 + 1.98 + passwd* pwd = getpwuid(getuid()); 1.99 + const char* home = pwd->pw_dir; 1.100 + path = home; 1.101 + path += "/.config/Oculus"; 1.102 + 1.103 + if (create_dir) 1.104 + { // Create the Oculus directory if it doesn't exist 1.105 + DIR* dir = opendir(path); 1.106 + if (dir == NULL) 1.107 + { 1.108 + mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO); 1.109 + } 1.110 + else 1.111 + { 1.112 + closedir(dir); 1.113 + } 1.114 + } 1.115 + 1.116 +#endif 1.117 + 1.118 + return path; 1.119 +} 1.120 + 1.121 +String GetProfilePath(bool create_dir) 1.122 +{ 1.123 + String path = GetBaseOVRPath(create_dir); 1.124 + path += "/Profiles.json"; 1.125 + return path; 1.126 +} 1.127 + 1.128 +//----------------------------------------------------------------------------- 1.129 +// ***** ProfileManager 1.130 + 1.131 +ProfileManager::ProfileManager() 1.132 +{ 1.133 + Changed = false; 1.134 + CacheDevice = Profile_Unknown; 1.135 +} 1.136 + 1.137 +ProfileManager::~ProfileManager() 1.138 +{ 1.139 + // If the profiles have been altered then write out the profile file 1.140 + if (Changed) 1.141 + SaveCache(); 1.142 + 1.143 + ClearCache(); 1.144 +} 1.145 + 1.146 +ProfileManager* ProfileManager::Create() 1.147 +{ 1.148 + return new ProfileManager(); 1.149 +} 1.150 + 1.151 +Profile* ProfileManager::CreateProfileObject(const char* user, 1.152 + ProfileType device, 1.153 + const char** device_name) 1.154 +{ 1.155 + Lock::Locker lockScope(&ProfileLock); 1.156 + 1.157 + Profile* profile = NULL; 1.158 + switch (device) 1.159 + { 1.160 + case Profile_RiftDK1: 1.161 + *device_name = "RiftDK1"; 1.162 + profile = new RiftDK1Profile(user); 1.163 + break; 1.164 + case Profile_RiftDKHD: 1.165 + case Profile_Unknown: 1.166 + break; 1.167 + } 1.168 + 1.169 + return profile; 1.170 +} 1.171 + 1.172 + 1.173 +// Clear the local profile cache 1.174 +void ProfileManager::ClearCache() 1.175 +{ 1.176 + Lock::Locker lockScope(&ProfileLock); 1.177 + 1.178 + ProfileCache.Clear(); 1.179 + CacheDevice = Profile_Unknown; 1.180 +} 1.181 + 1.182 + 1.183 +// Poplulates the local profile cache. This occurs on the first access of the profile 1.184 +// data. All profile operations are performed against the local cache until the 1.185 +// ProfileManager is released or goes out of scope at which time the cache is serialized 1.186 +// to disk. 1.187 +void ProfileManager::LoadCache(ProfileType device) 1.188 +{ 1.189 + Lock::Locker lockScope(&ProfileLock); 1.190 + 1.191 + ClearCache(); 1.192 + 1.193 + String path = GetProfilePath(false); 1.194 + 1.195 + Ptr<JSON> root = *JSON::Load(path); 1.196 + if (!root || root->GetItemCount() < 3) 1.197 + return; 1.198 + 1.199 + // First read the file type and version to make sure this is a valid file 1.200 + JSON* item0 = root->GetFirstItem(); 1.201 + JSON* item1 = root->GetNextItem(item0); 1.202 + JSON* item2 = root->GetNextItem(item1); 1.203 + 1.204 + if (OVR_strcmp(item0->Name, "Oculus Profile Version") == 0) 1.205 + { // In the future I may need to check versioning to determine parse method 1.206 + } 1.207 + else 1.208 + { 1.209 + return; 1.210 + } 1.211 + 1.212 + DefaultProfile = item1->Value; 1.213 + 1.214 + // Read the number of profiles 1.215 + int profileCount = (int)item2->dValue; 1.216 + JSON* profileItem = item2; 1.217 + 1.218 + for (int p=0; p<profileCount; p++) 1.219 + { 1.220 + profileItem = profileItem->GetNextItem(profileItem); 1.221 + if (!profileItem) 1.222 + break; 1.223 + 1.224 + // Read the required Name field 1.225 + const char* profileName; 1.226 + JSON* item = profileItem->GetFirstItem(); 1.227 + 1.228 + if (item && (OVR_strcmp(item->Name, "Name") == 0)) 1.229 + { 1.230 + profileName = item->Value; 1.231 + } 1.232 + else 1.233 + { 1.234 + return; // invalid field 1.235 + } 1.236 + 1.237 + const char* deviceName = 0; 1.238 + bool deviceFound = false; 1.239 + Ptr<Profile> profile = *CreateProfileObject(profileName, device, &deviceName); 1.240 + 1.241 + // Read the base profile fields. 1.242 + if (profile) 1.243 + { 1.244 + while (item = profileItem->GetNextItem(item), item) 1.245 + { 1.246 + if (item->Type != JSON_Object) 1.247 + { 1.248 + profile->ParseProperty(item->Name, item->Value); 1.249 + } 1.250 + else 1.251 + { // Search for the matching device to get device specific fields 1.252 + if (!deviceFound && OVR_strcmp(item->Name, deviceName) == 0) 1.253 + { 1.254 + deviceFound = true; 1.255 + 1.256 + for (JSON* deviceItem = item->GetFirstItem(); deviceItem; 1.257 + deviceItem = item->GetNextItem(deviceItem)) 1.258 + { 1.259 + profile->ParseProperty(deviceItem->Name, deviceItem->Value); 1.260 + } 1.261 + } 1.262 + } 1.263 + } 1.264 + } 1.265 + 1.266 + // Add the new profile 1.267 + if (deviceFound) 1.268 + ProfileCache.PushBack(profile); 1.269 + } 1.270 + 1.271 + CacheDevice = device; 1.272 +} 1.273 + 1.274 + 1.275 +// Serializes the profiles to disk. 1.276 +void ProfileManager::SaveCache() 1.277 +{ 1.278 + String path = GetProfilePath(true); 1.279 + 1.280 + Lock::Locker lockScope(&ProfileLock); 1.281 + 1.282 + // TODO: Since there is only a single device type now, a full tree overwrite 1.283 + // is sufficient but in the future a selective device branch replacement will 1.284 + // be necessary 1.285 + 1.286 + Ptr<JSON> root = *JSON::CreateObject(); 1.287 + root->AddNumberItem("Oculus Profile Version", PROFILE_VERSION); 1.288 + root->AddStringItem("CurrentProfile", DefaultProfile); 1.289 + root->AddNumberItem("ProfileCount", (double) ProfileCache.GetSize()); 1.290 + 1.291 + // Generate a JSON subtree for each profile 1.292 + for (unsigned int i=0; i<ProfileCache.GetSize(); i++) 1.293 + { 1.294 + Profile* profile = ProfileCache[i]; 1.295 + 1.296 + JSON* json_profile = JSON::CreateObject(); 1.297 + json_profile->Name = "Profile"; 1.298 + json_profile->AddStringItem("Name", profile->Name); 1.299 + const char* gender; 1.300 + switch (profile->GetGender()) 1.301 + { 1.302 + case Profile::Gender_Male: gender = "Male"; break; 1.303 + case Profile::Gender_Female: gender = "Female"; break; 1.304 + default: gender = "Unspecified"; 1.305 + } 1.306 + json_profile->AddStringItem("Gender", gender); 1.307 + json_profile->AddNumberItem("PlayerHeight", profile->PlayerHeight); 1.308 + json_profile->AddNumberItem("IPD", profile->IPD); 1.309 + 1.310 + if (profile->Type == Profile_RiftDK1) 1.311 + { 1.312 + RiftDK1Profile* riftdk1 = (RiftDK1Profile*)profile; 1.313 + JSON* json_riftdk1 = JSON::CreateObject(); 1.314 + json_profile->AddItem("RiftDK1", json_riftdk1); 1.315 + 1.316 + const char* eyecup = "A"; 1.317 + switch (riftdk1->EyeCups) 1.318 + { 1.319 + case RiftDK1Profile::EyeCup_A: eyecup = "A"; break; 1.320 + case RiftDK1Profile::EyeCup_B: eyecup = "B"; break; 1.321 + case RiftDK1Profile::EyeCup_C: eyecup = "C"; break; 1.322 + } 1.323 + json_riftdk1->AddStringItem("EyeCup", eyecup); 1.324 + json_riftdk1->AddNumberItem("LL", riftdk1->LL); 1.325 + json_riftdk1->AddNumberItem("LR", riftdk1->LR); 1.326 + json_riftdk1->AddNumberItem("RL", riftdk1->RL); 1.327 + json_riftdk1->AddNumberItem("RR", riftdk1->RR); 1.328 + } 1.329 + 1.330 + root->AddItem("Profile", json_profile); 1.331 + } 1.332 + 1.333 + root->Save(path); 1.334 +} 1.335 + 1.336 +// Returns the number of stored profiles for this device type 1.337 +int ProfileManager::GetProfileCount(ProfileType device) 1.338 +{ 1.339 + Lock::Locker lockScope(&ProfileLock); 1.340 + 1.341 + if (CacheDevice == Profile_Unknown) 1.342 + LoadCache(device); 1.343 + 1.344 + return (int)ProfileCache.GetSize(); 1.345 +} 1.346 + 1.347 +// Returns the profile name of a specific profile in the list. The returned 1.348 +// memory is locally allocated and should not be stored or deleted. Returns NULL 1.349 +// if the index is invalid 1.350 +const char* ProfileManager::GetProfileName(ProfileType device, unsigned int index) 1.351 +{ 1.352 + Lock::Locker lockScope(&ProfileLock); 1.353 + 1.354 + if (CacheDevice == Profile_Unknown) 1.355 + LoadCache(device); 1.356 + 1.357 + if (index < ProfileCache.GetSize()) 1.358 + { 1.359 + Profile* profile = ProfileCache[index]; 1.360 + OVR_strcpy(NameBuff, Profile::MaxNameLen, profile->Name); 1.361 + return NameBuff; 1.362 + } 1.363 + else 1.364 + { 1.365 + return NULL; 1.366 + } 1.367 +} 1.368 + 1.369 +bool ProfileManager::HasProfile(ProfileType device, const char* name) 1.370 +{ 1.371 + Lock::Locker lockScope(&ProfileLock); 1.372 + 1.373 + if (CacheDevice == Profile_Unknown) 1.374 + LoadCache(device); 1.375 + 1.376 + for (unsigned i = 0; i< ProfileCache.GetSize(); i++) 1.377 + { 1.378 + if (ProfileCache[i] && OVR_strcmp(ProfileCache[i]->Name, name) == 0) 1.379 + return true; 1.380 + } 1.381 + return false; 1.382 +} 1.383 + 1.384 + 1.385 +// Returns a specific profile object in the list. The returned memory should be 1.386 +// encapsulated in a Ptr<> object or released after use. Returns NULL if the index 1.387 +// is invalid 1.388 +Profile* ProfileManager::LoadProfile(ProfileType device, unsigned int index) 1.389 +{ 1.390 + Lock::Locker lockScope(&ProfileLock); 1.391 + 1.392 + if (CacheDevice == Profile_Unknown) 1.393 + LoadCache(device); 1.394 + 1.395 + if (index < ProfileCache.GetSize()) 1.396 + { 1.397 + Profile* profile = ProfileCache[index]; 1.398 + return profile->Clone(); 1.399 + } 1.400 + else 1.401 + { 1.402 + return NULL; 1.403 + } 1.404 +} 1.405 + 1.406 +// Returns a profile object for a particular device and user name. The returned 1.407 +// memory should be encapsulated in a Ptr<> object or released after use. Returns 1.408 +// NULL if the profile is not found 1.409 +Profile* ProfileManager::LoadProfile(ProfileType device, const char* user) 1.410 +{ 1.411 + if (user == NULL) 1.412 + return NULL; 1.413 + 1.414 + Lock::Locker lockScope(&ProfileLock); 1.415 + 1.416 + if (CacheDevice == Profile_Unknown) 1.417 + LoadCache(device); 1.418 + 1.419 + for (unsigned int i=0; i<ProfileCache.GetSize(); i++) 1.420 + { 1.421 + if (OVR_strcmp(user, ProfileCache[i]->Name) == 0) 1.422 + { // Found the requested user profile 1.423 + Profile* profile = ProfileCache[i]; 1.424 + return profile->Clone(); 1.425 + } 1.426 + } 1.427 + 1.428 + return NULL; 1.429 +} 1.430 + 1.431 +// Returns a profile with all system default values 1.432 +Profile* ProfileManager::GetDeviceDefaultProfile(ProfileType device) 1.433 +{ 1.434 + const char* device_name = NULL; 1.435 + return CreateProfileObject("default", device, &device_name); 1.436 +} 1.437 + 1.438 +// Returns the name of the profile that is marked as the current default user. 1.439 +const char* ProfileManager::GetDefaultProfileName(ProfileType device) 1.440 +{ 1.441 + Lock::Locker lockScope(&ProfileLock); 1.442 + 1.443 + if (CacheDevice == Profile_Unknown) 1.444 + LoadCache(device); 1.445 + 1.446 + if (ProfileCache.GetSize() > 0) 1.447 + { 1.448 + OVR_strcpy(NameBuff, Profile::MaxNameLen, DefaultProfile); 1.449 + return NameBuff; 1.450 + } 1.451 + else 1.452 + { 1.453 + return NULL; 1.454 + } 1.455 +} 1.456 + 1.457 +// Marks a particular user as the current default user. 1.458 +bool ProfileManager::SetDefaultProfileName(ProfileType device, const char* name) 1.459 +{ 1.460 + Lock::Locker lockScope(&ProfileLock); 1.461 + 1.462 + if (CacheDevice == Profile_Unknown) 1.463 + LoadCache(device); 1.464 +// TODO: I should verify that the user is valid 1.465 + if (ProfileCache.GetSize() > 0) 1.466 + { 1.467 + DefaultProfile = name; 1.468 + Changed = true; 1.469 + return true; 1.470 + } 1.471 + else 1.472 + { 1.473 + return false; 1.474 + } 1.475 +} 1.476 + 1.477 + 1.478 +// Saves a new or existing profile. Returns true on success or false on an 1.479 +// invalid or failed save. 1.480 +bool ProfileManager::Save(const Profile* profile) 1.481 +{ 1.482 + Lock::Locker lockScope(&ProfileLock); 1.483 + 1.484 + if (OVR_strcmp(profile->Name, "default") == 0) 1.485 + return false; // don't save a default profile 1.486 + 1.487 + // TODO: I should also verify that this profile type matches the current cache 1.488 + if (CacheDevice == Profile_Unknown) 1.489 + LoadCache(profile->Type); 1.490 + 1.491 + // Look for the pre-existence of this profile 1.492 + bool added = false; 1.493 + for (unsigned int i=0; i<ProfileCache.GetSize(); i++) 1.494 + { 1.495 + int compare = OVR_strcmp(profile->Name, ProfileCache[i]->Name); 1.496 + 1.497 + if (compare == 0) 1.498 + { 1.499 + // TODO: I should do a proper field comparison to avoid unnecessary 1.500 + // overwrites and file saves 1.501 + 1.502 + // Replace the previous instance with the new profile 1.503 + ProfileCache[i] = *profile->Clone(); 1.504 + added = true; 1.505 + Changed = true; 1.506 + break; 1.507 + } 1.508 + } 1.509 + 1.510 + if (!added) 1.511 + { 1.512 + ProfileCache.PushBack(*profile->Clone()); 1.513 + if (ProfileCache.GetSize() == 1) 1.514 + CacheDevice = profile->Type; 1.515 + 1.516 + Changed = true; 1.517 + } 1.518 + 1.519 + return true; 1.520 +} 1.521 + 1.522 +// Removes an existing profile. Returns true if the profile was found and deleted 1.523 +// and returns false otherwise. 1.524 +bool ProfileManager::Delete(const Profile* profile) 1.525 +{ 1.526 + Lock::Locker lockScope(&ProfileLock); 1.527 + 1.528 + if (OVR_strcmp(profile->Name, "default") == 0) 1.529 + return false; // don't delete a default profile 1.530 + 1.531 + if (CacheDevice == Profile_Unknown) 1.532 + LoadCache(profile->Type); 1.533 + 1.534 + // Look for the existence of this profile 1.535 + for (unsigned int i=0; i<ProfileCache.GetSize(); i++) 1.536 + { 1.537 + if (OVR_strcmp(profile->Name, ProfileCache[i]->Name) == 0) 1.538 + { 1.539 + if (OVR_strcmp(profile->Name, DefaultProfile) == 0) 1.540 + DefaultProfile.Clear(); 1.541 + 1.542 + ProfileCache.RemoveAt(i); 1.543 + Changed = true; 1.544 + return true; 1.545 + } 1.546 + } 1.547 + 1.548 + return false; 1.549 +} 1.550 + 1.551 + 1.552 + 1.553 +//----------------------------------------------------------------------------- 1.554 +// ***** Profile 1.555 + 1.556 +Profile::Profile(ProfileType device, const char* name) 1.557 +{ 1.558 + Type = device; 1.559 + Gender = Gender_Unspecified; 1.560 + PlayerHeight = 1.778f; // 5'10" inch man 1.561 + IPD = 0.064f; 1.562 + OVR_strcpy(Name, MaxNameLen, name); 1.563 +} 1.564 + 1.565 + 1.566 +bool Profile::ParseProperty(const char* prop, const char* sval) 1.567 +{ 1.568 + if (OVR_strcmp(prop, "Name") == 0) 1.569 + { 1.570 + OVR_strcpy(Name, MaxNameLen, sval); 1.571 + return true; 1.572 + } 1.573 + else if (OVR_strcmp(prop, "Gender") == 0) 1.574 + { 1.575 + if (OVR_strcmp(sval, "Male") == 0) 1.576 + Gender = Gender_Male; 1.577 + else if (OVR_strcmp(sval, "Female") == 0) 1.578 + Gender = Gender_Female; 1.579 + else 1.580 + Gender = Gender_Unspecified; 1.581 + 1.582 + return true; 1.583 + } 1.584 + else if (OVR_strcmp(prop, "PlayerHeight") == 0) 1.585 + { 1.586 + PlayerHeight = (float)atof(sval); 1.587 + return true; 1.588 + } 1.589 + else if (OVR_strcmp(prop, "IPD") == 0) 1.590 + { 1.591 + IPD = (float)atof(sval); 1.592 + return true; 1.593 + } 1.594 + 1.595 + return false; 1.596 +} 1.597 + 1.598 + 1.599 +// Computes the eye height from the metric head height 1.600 +float Profile::GetEyeHeight() 1.601 +{ 1.602 + const float EYE_TO_HEADTOP_RATIO = 0.44538f; 1.603 + const float MALE_AVG_HEAD_HEIGHT = 0.232f; 1.604 + const float FEMALE_AVG_HEAD_HEIGHT = 0.218f; 1.605 + 1.606 + // compute distance from top of skull to the eye 1.607 + float head_height; 1.608 + if (Gender == Gender_Female) 1.609 + head_height = FEMALE_AVG_HEAD_HEIGHT; 1.610 + else 1.611 + head_height = MALE_AVG_HEAD_HEIGHT; 1.612 + 1.613 + float skull = EYE_TO_HEADTOP_RATIO * head_height; 1.614 + 1.615 + float eye_height = PlayerHeight - skull; 1.616 + return eye_height; 1.617 +} 1.618 + 1.619 + 1.620 +//----------------------------------------------------------------------------- 1.621 +// ***** RiftDK1Profile 1.622 + 1.623 +RiftDK1Profile::RiftDK1Profile(const char* name) : Profile(Profile_RiftDK1, name) 1.624 +{ 1.625 + EyeCups = EyeCup_A; 1.626 + LL = 0; 1.627 + LR = 0; 1.628 + RL = 0; 1.629 + RR = 0; 1.630 +} 1.631 + 1.632 +bool RiftDK1Profile::ParseProperty(const char* prop, const char* sval) 1.633 +{ 1.634 + if (OVR_strcmp(prop, "EyeCup") == 0) 1.635 + { 1.636 + switch (sval[0]) 1.637 + { 1.638 + case 'C': EyeCups = EyeCup_C; break; 1.639 + case 'B': EyeCups = EyeCup_B; break; 1.640 + default: EyeCups = EyeCup_A; break; 1.641 + } 1.642 + return true; 1.643 + } 1.644 + else if (OVR_strcmp(prop, "LL") == 0) 1.645 + { 1.646 + LL = atoi(sval); 1.647 + return true; 1.648 + } 1.649 + else if (OVR_strcmp(prop, "LR") == 0) 1.650 + { 1.651 + LR = atoi(sval); 1.652 + return true; 1.653 + } 1.654 + else if (OVR_strcmp(prop, "RL") == 0) 1.655 + { 1.656 + RL = atoi(sval); 1.657 + return true; 1.658 + } 1.659 + else if (OVR_strcmp(prop, "RR") == 0) 1.660 + { 1.661 + RR = atoi(sval); 1.662 + return true; 1.663 + } 1.664 + 1.665 + return Profile::ParseProperty(prop, sval); 1.666 +} 1.667 + 1.668 +Profile* RiftDK1Profile::Clone() const 1.669 +{ 1.670 + RiftDK1Profile* profile = new RiftDK1Profile(*this); 1.671 + return profile; 1.672 +} 1.673 + 1.674 +} // OVR