nuclear@1: /************************************************************************************ nuclear@1: nuclear@1: PublicHeader: None nuclear@1: Filename : OVR_Profile.cpp nuclear@1: Content : Structs and functions for loading and storing device profile settings nuclear@1: Created : February 14, 2013 nuclear@1: Notes : nuclear@1: nuclear@1: Profiles are used to store per-user settings that can be transferred and used nuclear@1: across multiple applications. For example, player IPD can be configured once nuclear@1: and reused for a unified experience across games. Configuration and saving of profiles nuclear@1: can be accomplished in game via the Profile API or by the official Oculus Configuration nuclear@1: Utility. nuclear@1: nuclear@1: Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. nuclear@1: nuclear@1: Use of this software is subject to the terms of the Oculus license nuclear@1: agreement provided at the time of installation or download, or which nuclear@1: otherwise accompanies this software in either electronic or hard copy form. nuclear@1: nuclear@1: ************************************************************************************/ nuclear@1: nuclear@1: #include "OVR_Profile.h" nuclear@1: #include "OVR_JSON.h" nuclear@1: #include "Kernel/OVR_Types.h" nuclear@1: #include "Kernel/OVR_SysFile.h" nuclear@1: #include "Kernel/OVR_Allocator.h" nuclear@1: #include "Kernel/OVR_Array.h" nuclear@1: nuclear@1: #ifdef OVR_OS_WIN32 nuclear@1: #include nuclear@1: #else nuclear@1: #include nuclear@1: #include nuclear@1: nuclear@1: #ifdef OVR_OS_LINUX nuclear@1: #include nuclear@1: #include nuclear@1: #endif nuclear@1: nuclear@1: #endif nuclear@1: nuclear@1: #define PROFILE_VERSION 1.0 nuclear@1: nuclear@1: namespace OVR { nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // Returns the pathname of the JSON file containing the stored profiles nuclear@1: String GetBaseOVRPath(bool create_dir) nuclear@1: { nuclear@1: String path; nuclear@1: nuclear@1: #if defined(OVR_OS_WIN32) nuclear@1: nuclear@1: TCHAR data_path[MAX_PATH]; nuclear@1: SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, NULL, 0, data_path); nuclear@1: path = String(data_path); nuclear@1: nuclear@1: path += "/Oculus"; nuclear@1: nuclear@1: if (create_dir) nuclear@1: { // Create the Oculus directory if it doesn't exist nuclear@1: WCHAR wpath[128]; nuclear@1: OVR::UTF8Util::DecodeString(wpath, path.ToCStr()); nuclear@1: nuclear@1: DWORD attrib = GetFileAttributes(wpath); nuclear@1: bool exists = attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY); nuclear@1: if (!exists) nuclear@1: { nuclear@1: CreateDirectory(wpath, NULL); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: #elif defined(OVR_OS_MAC) nuclear@1: nuclear@1: const char* home = getenv("HOME"); nuclear@1: path = home; nuclear@1: path += "/Library/Preferences/Oculus"; nuclear@1: nuclear@1: if (create_dir) nuclear@1: { // Create the Oculus directory if it doesn't exist nuclear@1: DIR* dir = opendir(path); nuclear@1: if (dir == NULL) nuclear@1: { nuclear@1: mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO); nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: closedir(dir); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: #else nuclear@1: nuclear@1: passwd* pwd = getpwuid(getuid()); nuclear@1: const char* home = pwd->pw_dir; nuclear@1: path = home; nuclear@1: path += "/.config/Oculus"; nuclear@1: nuclear@1: if (create_dir) nuclear@1: { // Create the Oculus directory if it doesn't exist nuclear@1: DIR* dir = opendir(path); nuclear@1: if (dir == NULL) nuclear@1: { nuclear@1: mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO); nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: closedir(dir); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: #endif nuclear@1: nuclear@1: return path; nuclear@1: } nuclear@1: nuclear@1: String GetProfilePath(bool create_dir) nuclear@1: { nuclear@1: String path = GetBaseOVRPath(create_dir); nuclear@1: path += "/Profiles.json"; nuclear@1: return path; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // ***** ProfileManager nuclear@1: nuclear@1: ProfileManager::ProfileManager() nuclear@1: { nuclear@1: Changed = false; nuclear@1: CacheDevice = Profile_Unknown; nuclear@1: } nuclear@1: nuclear@1: ProfileManager::~ProfileManager() nuclear@1: { nuclear@1: // If the profiles have been altered then write out the profile file nuclear@1: if (Changed) nuclear@1: SaveCache(); nuclear@1: nuclear@1: ClearCache(); nuclear@1: } nuclear@1: nuclear@1: ProfileManager* ProfileManager::Create() nuclear@1: { nuclear@1: return new ProfileManager(); nuclear@1: } nuclear@1: nuclear@1: Profile* ProfileManager::CreateProfileObject(const char* user, nuclear@1: ProfileType device, nuclear@1: const char** device_name) nuclear@1: { nuclear@1: Lock::Locker lockScope(&ProfileLock); nuclear@1: nuclear@1: Profile* profile = NULL; nuclear@1: switch (device) nuclear@1: { nuclear@1: case Profile_RiftDK1: nuclear@1: *device_name = "RiftDK1"; nuclear@1: profile = new RiftDK1Profile(user); nuclear@1: break; nuclear@1: case Profile_RiftDKHD: nuclear@1: case Profile_Unknown: nuclear@1: break; nuclear@1: } nuclear@1: nuclear@1: return profile; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: // Clear the local profile cache nuclear@1: void ProfileManager::ClearCache() nuclear@1: { nuclear@1: Lock::Locker lockScope(&ProfileLock); nuclear@1: nuclear@1: ProfileCache.Clear(); nuclear@1: CacheDevice = Profile_Unknown; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: // Poplulates the local profile cache. This occurs on the first access of the profile nuclear@1: // data. All profile operations are performed against the local cache until the nuclear@1: // ProfileManager is released or goes out of scope at which time the cache is serialized nuclear@1: // to disk. nuclear@1: void ProfileManager::LoadCache(ProfileType device) nuclear@1: { nuclear@1: Lock::Locker lockScope(&ProfileLock); nuclear@1: nuclear@1: ClearCache(); nuclear@1: nuclear@1: String path = GetProfilePath(false); nuclear@1: nuclear@1: Ptr root = *JSON::Load(path); nuclear@1: if (!root || root->GetItemCount() < 3) nuclear@1: return; nuclear@1: nuclear@1: // First read the file type and version to make sure this is a valid file nuclear@1: JSON* item0 = root->GetFirstItem(); nuclear@1: JSON* item1 = root->GetNextItem(item0); nuclear@1: JSON* item2 = root->GetNextItem(item1); nuclear@1: nuclear@1: if (OVR_strcmp(item0->Name, "Oculus Profile Version") == 0) nuclear@1: { // In the future I may need to check versioning to determine parse method nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: return; nuclear@1: } nuclear@1: nuclear@1: DefaultProfile = item1->Value; nuclear@1: nuclear@1: // Read the number of profiles nuclear@1: int profileCount = (int)item2->dValue; nuclear@1: JSON* profileItem = item2; nuclear@1: nuclear@1: for (int p=0; pGetNextItem(profileItem); nuclear@1: if (!profileItem) nuclear@1: break; nuclear@1: nuclear@1: // Read the required Name field nuclear@1: const char* profileName; nuclear@1: JSON* item = profileItem->GetFirstItem(); nuclear@1: nuclear@1: if (item && (OVR_strcmp(item->Name, "Name") == 0)) nuclear@1: { nuclear@1: profileName = item->Value; nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: return; // invalid field nuclear@1: } nuclear@1: nuclear@1: const char* deviceName = 0; nuclear@1: bool deviceFound = false; nuclear@1: Ptr profile = *CreateProfileObject(profileName, device, &deviceName); nuclear@1: nuclear@1: // Read the base profile fields. nuclear@1: if (profile) nuclear@1: { nuclear@1: while (item = profileItem->GetNextItem(item), item) nuclear@1: { nuclear@1: if (item->Type != JSON_Object) nuclear@1: { nuclear@1: profile->ParseProperty(item->Name, item->Value); nuclear@1: } nuclear@1: else nuclear@1: { // Search for the matching device to get device specific fields nuclear@1: if (!deviceFound && OVR_strcmp(item->Name, deviceName) == 0) nuclear@1: { nuclear@1: deviceFound = true; nuclear@1: nuclear@1: for (JSON* deviceItem = item->GetFirstItem(); deviceItem; nuclear@1: deviceItem = item->GetNextItem(deviceItem)) nuclear@1: { nuclear@1: profile->ParseProperty(deviceItem->Name, deviceItem->Value); nuclear@1: } nuclear@1: } nuclear@1: } nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: // Add the new profile nuclear@1: if (deviceFound) nuclear@1: ProfileCache.PushBack(profile); nuclear@1: } nuclear@1: nuclear@1: CacheDevice = device; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: // Serializes the profiles to disk. nuclear@1: void ProfileManager::SaveCache() nuclear@1: { nuclear@1: String path = GetProfilePath(true); nuclear@1: nuclear@1: Lock::Locker lockScope(&ProfileLock); nuclear@1: nuclear@1: // TODO: Since there is only a single device type now, a full tree overwrite nuclear@1: // is sufficient but in the future a selective device branch replacement will nuclear@1: // be necessary nuclear@1: nuclear@1: Ptr root = *JSON::CreateObject(); nuclear@1: root->AddNumberItem("Oculus Profile Version", PROFILE_VERSION); nuclear@1: root->AddStringItem("CurrentProfile", DefaultProfile); nuclear@1: root->AddNumberItem("ProfileCount", (double) ProfileCache.GetSize()); nuclear@1: nuclear@1: // Generate a JSON subtree for each profile nuclear@1: for (unsigned int i=0; iName = "Profile"; nuclear@1: json_profile->AddStringItem("Name", profile->Name); nuclear@1: const char* gender; nuclear@1: switch (profile->GetGender()) nuclear@1: { nuclear@1: case Profile::Gender_Male: gender = "Male"; break; nuclear@1: case Profile::Gender_Female: gender = "Female"; break; nuclear@1: default: gender = "Unspecified"; nuclear@1: } nuclear@1: json_profile->AddStringItem("Gender", gender); nuclear@1: json_profile->AddNumberItem("PlayerHeight", profile->PlayerHeight); nuclear@1: json_profile->AddNumberItem("IPD", profile->IPD); nuclear@1: nuclear@1: if (profile->Type == Profile_RiftDK1) nuclear@1: { nuclear@1: RiftDK1Profile* riftdk1 = (RiftDK1Profile*)profile; nuclear@1: JSON* json_riftdk1 = JSON::CreateObject(); nuclear@1: json_profile->AddItem("RiftDK1", json_riftdk1); nuclear@1: nuclear@1: const char* eyecup = "A"; nuclear@1: switch (riftdk1->EyeCups) nuclear@1: { nuclear@1: case RiftDK1Profile::EyeCup_A: eyecup = "A"; break; nuclear@1: case RiftDK1Profile::EyeCup_B: eyecup = "B"; break; nuclear@1: case RiftDK1Profile::EyeCup_C: eyecup = "C"; break; nuclear@1: } nuclear@1: json_riftdk1->AddStringItem("EyeCup", eyecup); nuclear@1: json_riftdk1->AddNumberItem("LL", riftdk1->LL); nuclear@1: json_riftdk1->AddNumberItem("LR", riftdk1->LR); nuclear@1: json_riftdk1->AddNumberItem("RL", riftdk1->RL); nuclear@1: json_riftdk1->AddNumberItem("RR", riftdk1->RR); nuclear@1: } nuclear@1: nuclear@1: root->AddItem("Profile", json_profile); nuclear@1: } nuclear@1: nuclear@1: root->Save(path); nuclear@1: } nuclear@1: nuclear@1: // Returns the number of stored profiles for this device type nuclear@1: int ProfileManager::GetProfileCount(ProfileType device) nuclear@1: { nuclear@1: Lock::Locker lockScope(&ProfileLock); nuclear@1: nuclear@1: if (CacheDevice == Profile_Unknown) nuclear@1: LoadCache(device); nuclear@1: nuclear@1: return (int)ProfileCache.GetSize(); nuclear@1: } nuclear@1: nuclear@1: // Returns the profile name of a specific profile in the list. The returned nuclear@1: // memory is locally allocated and should not be stored or deleted. Returns NULL nuclear@1: // if the index is invalid nuclear@1: const char* ProfileManager::GetProfileName(ProfileType device, unsigned int index) nuclear@1: { nuclear@1: Lock::Locker lockScope(&ProfileLock); nuclear@1: nuclear@1: if (CacheDevice == Profile_Unknown) nuclear@1: LoadCache(device); nuclear@1: nuclear@1: if (index < ProfileCache.GetSize()) nuclear@1: { nuclear@1: Profile* profile = ProfileCache[index]; nuclear@1: OVR_strcpy(NameBuff, Profile::MaxNameLen, profile->Name); nuclear@1: return NameBuff; nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: return NULL; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: bool ProfileManager::HasProfile(ProfileType device, const char* name) nuclear@1: { nuclear@1: Lock::Locker lockScope(&ProfileLock); nuclear@1: nuclear@1: if (CacheDevice == Profile_Unknown) nuclear@1: LoadCache(device); nuclear@1: nuclear@1: for (unsigned i = 0; i< ProfileCache.GetSize(); i++) nuclear@1: { nuclear@1: if (ProfileCache[i] && OVR_strcmp(ProfileCache[i]->Name, name) == 0) nuclear@1: return true; nuclear@1: } nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: // Returns a specific profile object in the list. The returned memory should be nuclear@1: // encapsulated in a Ptr<> object or released after use. Returns NULL if the index nuclear@1: // is invalid nuclear@1: Profile* ProfileManager::LoadProfile(ProfileType device, unsigned int index) nuclear@1: { nuclear@1: Lock::Locker lockScope(&ProfileLock); nuclear@1: nuclear@1: if (CacheDevice == Profile_Unknown) nuclear@1: LoadCache(device); nuclear@1: nuclear@1: if (index < ProfileCache.GetSize()) nuclear@1: { nuclear@1: Profile* profile = ProfileCache[index]; nuclear@1: return profile->Clone(); nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: return NULL; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: // Returns a profile object for a particular device and user name. The returned nuclear@1: // memory should be encapsulated in a Ptr<> object or released after use. Returns nuclear@1: // NULL if the profile is not found nuclear@1: Profile* ProfileManager::LoadProfile(ProfileType device, const char* user) nuclear@1: { nuclear@1: if (user == NULL) nuclear@1: return NULL; nuclear@1: nuclear@1: Lock::Locker lockScope(&ProfileLock); nuclear@1: nuclear@1: if (CacheDevice == Profile_Unknown) nuclear@1: LoadCache(device); nuclear@1: nuclear@1: for (unsigned int i=0; iName) == 0) nuclear@1: { // Found the requested user profile nuclear@1: Profile* profile = ProfileCache[i]; nuclear@1: return profile->Clone(); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: return NULL; nuclear@1: } nuclear@1: nuclear@1: // Returns a profile with all system default values nuclear@1: Profile* ProfileManager::GetDeviceDefaultProfile(ProfileType device) nuclear@1: { nuclear@1: const char* device_name = NULL; nuclear@1: return CreateProfileObject("default", device, &device_name); nuclear@1: } nuclear@1: nuclear@1: // Returns the name of the profile that is marked as the current default user. nuclear@1: const char* ProfileManager::GetDefaultProfileName(ProfileType device) nuclear@1: { nuclear@1: Lock::Locker lockScope(&ProfileLock); nuclear@1: nuclear@1: if (CacheDevice == Profile_Unknown) nuclear@1: LoadCache(device); nuclear@1: nuclear@1: if (ProfileCache.GetSize() > 0) nuclear@1: { nuclear@1: OVR_strcpy(NameBuff, Profile::MaxNameLen, DefaultProfile); nuclear@1: return NameBuff; nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: return NULL; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: // Marks a particular user as the current default user. nuclear@1: bool ProfileManager::SetDefaultProfileName(ProfileType device, const char* name) nuclear@1: { nuclear@1: Lock::Locker lockScope(&ProfileLock); nuclear@1: nuclear@1: if (CacheDevice == Profile_Unknown) nuclear@1: LoadCache(device); nuclear@1: // TODO: I should verify that the user is valid nuclear@1: if (ProfileCache.GetSize() > 0) nuclear@1: { nuclear@1: DefaultProfile = name; nuclear@1: Changed = true; nuclear@1: return true; nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: nuclear@1: // Saves a new or existing profile. Returns true on success or false on an nuclear@1: // invalid or failed save. nuclear@1: bool ProfileManager::Save(const Profile* profile) nuclear@1: { nuclear@1: Lock::Locker lockScope(&ProfileLock); nuclear@1: nuclear@1: if (OVR_strcmp(profile->Name, "default") == 0) nuclear@1: return false; // don't save a default profile nuclear@1: nuclear@1: // TODO: I should also verify that this profile type matches the current cache nuclear@1: if (CacheDevice == Profile_Unknown) nuclear@1: LoadCache(profile->Type); nuclear@1: nuclear@1: // Look for the pre-existence of this profile nuclear@1: bool added = false; nuclear@1: for (unsigned int i=0; iName, ProfileCache[i]->Name); nuclear@1: nuclear@1: if (compare == 0) nuclear@1: { nuclear@1: // TODO: I should do a proper field comparison to avoid unnecessary nuclear@1: // overwrites and file saves nuclear@1: nuclear@1: // Replace the previous instance with the new profile nuclear@1: ProfileCache[i] = *profile->Clone(); nuclear@1: added = true; nuclear@1: Changed = true; nuclear@1: break; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: if (!added) nuclear@1: { nuclear@1: ProfileCache.PushBack(*profile->Clone()); nuclear@1: if (ProfileCache.GetSize() == 1) nuclear@1: CacheDevice = profile->Type; nuclear@1: nuclear@1: Changed = true; nuclear@1: } nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: // Removes an existing profile. Returns true if the profile was found and deleted nuclear@1: // and returns false otherwise. nuclear@1: bool ProfileManager::Delete(const Profile* profile) nuclear@1: { nuclear@1: Lock::Locker lockScope(&ProfileLock); nuclear@1: nuclear@1: if (OVR_strcmp(profile->Name, "default") == 0) nuclear@1: return false; // don't delete a default profile nuclear@1: nuclear@1: if (CacheDevice == Profile_Unknown) nuclear@1: LoadCache(profile->Type); nuclear@1: nuclear@1: // Look for the existence of this profile nuclear@1: for (unsigned int i=0; iName, ProfileCache[i]->Name) == 0) nuclear@1: { nuclear@1: if (OVR_strcmp(profile->Name, DefaultProfile) == 0) nuclear@1: DefaultProfile.Clear(); nuclear@1: nuclear@1: ProfileCache.RemoveAt(i); nuclear@1: Changed = true; nuclear@1: return true; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // ***** Profile nuclear@1: nuclear@1: Profile::Profile(ProfileType device, const char* name) nuclear@1: { nuclear@1: Type = device; nuclear@1: Gender = Gender_Unspecified; nuclear@1: PlayerHeight = 1.778f; // 5'10" inch man nuclear@1: IPD = 0.064f; nuclear@1: OVR_strcpy(Name, MaxNameLen, name); nuclear@1: } nuclear@1: nuclear@1: nuclear@1: bool Profile::ParseProperty(const char* prop, const char* sval) nuclear@1: { nuclear@1: if (OVR_strcmp(prop, "Name") == 0) nuclear@1: { nuclear@1: OVR_strcpy(Name, MaxNameLen, sval); nuclear@1: return true; nuclear@1: } nuclear@1: else if (OVR_strcmp(prop, "Gender") == 0) nuclear@1: { nuclear@1: if (OVR_strcmp(sval, "Male") == 0) nuclear@1: Gender = Gender_Male; nuclear@1: else if (OVR_strcmp(sval, "Female") == 0) nuclear@1: Gender = Gender_Female; nuclear@1: else nuclear@1: Gender = Gender_Unspecified; nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: else if (OVR_strcmp(prop, "PlayerHeight") == 0) nuclear@1: { nuclear@1: PlayerHeight = (float)atof(sval); nuclear@1: return true; nuclear@1: } nuclear@1: else if (OVR_strcmp(prop, "IPD") == 0) nuclear@1: { nuclear@1: IPD = (float)atof(sval); nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: // Computes the eye height from the metric head height nuclear@1: float Profile::GetEyeHeight() nuclear@1: { nuclear@1: const float EYE_TO_HEADTOP_RATIO = 0.44538f; nuclear@1: const float MALE_AVG_HEAD_HEIGHT = 0.232f; nuclear@1: const float FEMALE_AVG_HEAD_HEIGHT = 0.218f; nuclear@1: nuclear@1: // compute distance from top of skull to the eye nuclear@1: float head_height; nuclear@1: if (Gender == Gender_Female) nuclear@1: head_height = FEMALE_AVG_HEAD_HEIGHT; nuclear@1: else nuclear@1: head_height = MALE_AVG_HEAD_HEIGHT; nuclear@1: nuclear@1: float skull = EYE_TO_HEADTOP_RATIO * head_height; nuclear@1: nuclear@1: float eye_height = PlayerHeight - skull; nuclear@1: return eye_height; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // ***** RiftDK1Profile nuclear@1: nuclear@1: RiftDK1Profile::RiftDK1Profile(const char* name) : Profile(Profile_RiftDK1, name) nuclear@1: { nuclear@1: EyeCups = EyeCup_A; nuclear@1: LL = 0; nuclear@1: LR = 0; nuclear@1: RL = 0; nuclear@1: RR = 0; nuclear@1: } nuclear@1: nuclear@1: bool RiftDK1Profile::ParseProperty(const char* prop, const char* sval) nuclear@1: { nuclear@1: if (OVR_strcmp(prop, "EyeCup") == 0) nuclear@1: { nuclear@1: switch (sval[0]) nuclear@1: { nuclear@1: case 'C': EyeCups = EyeCup_C; break; nuclear@1: case 'B': EyeCups = EyeCup_B; break; nuclear@1: default: EyeCups = EyeCup_A; break; nuclear@1: } nuclear@1: return true; nuclear@1: } nuclear@1: else if (OVR_strcmp(prop, "LL") == 0) nuclear@1: { nuclear@1: LL = atoi(sval); nuclear@1: return true; nuclear@1: } nuclear@1: else if (OVR_strcmp(prop, "LR") == 0) nuclear@1: { nuclear@1: LR = atoi(sval); nuclear@1: return true; nuclear@1: } nuclear@1: else if (OVR_strcmp(prop, "RL") == 0) nuclear@1: { nuclear@1: RL = atoi(sval); nuclear@1: return true; nuclear@1: } nuclear@1: else if (OVR_strcmp(prop, "RR") == 0) nuclear@1: { nuclear@1: RR = atoi(sval); nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: return Profile::ParseProperty(prop, sval); nuclear@1: } nuclear@1: nuclear@1: Profile* RiftDK1Profile::Clone() const nuclear@1: { nuclear@1: RiftDK1Profile* profile = new RiftDK1Profile(*this); nuclear@1: return profile; nuclear@1: } nuclear@1: nuclear@1: } // OVR