nuclear@0: /************************************************************************************ nuclear@0: nuclear@0: PublicHeader: None nuclear@0: Filename : OVR_Profile.cpp nuclear@0: Content : Structs and functions for loading and storing device profile settings nuclear@0: Created : February 14, 2013 nuclear@0: Notes : nuclear@0: nuclear@0: Profiles are used to store per-user settings that can be transferred and used nuclear@0: across multiple applications. For example, player IPD can be configured once nuclear@0: and reused for a unified experience across games. Configuration and saving of profiles nuclear@0: can be accomplished in game via the Profile API or by the official Oculus Configuration nuclear@0: Utility. nuclear@0: nuclear@0: Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. nuclear@0: nuclear@0: Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); nuclear@0: you may not use the Oculus VR Rift SDK except in compliance with the License, nuclear@0: which is provided at the time of installation or download, or which nuclear@0: otherwise accompanies this software in either electronic or hard copy form. nuclear@0: nuclear@0: You may obtain a copy of the License at nuclear@0: nuclear@0: http://www.oculusvr.com/licenses/LICENSE-3.2 nuclear@0: nuclear@0: Unless required by applicable law or agreed to in writing, the Oculus VR SDK nuclear@0: distributed under the License is distributed on an "AS IS" BASIS, nuclear@0: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. nuclear@0: See the License for the specific language governing permissions and nuclear@0: limitations under the License. nuclear@0: nuclear@0: ************************************************************************************/ nuclear@0: nuclear@0: #include "OVR_Profile.h" nuclear@0: #include "OVR_JSON.h" nuclear@0: #include "Kernel/OVR_SysFile.h" nuclear@0: #include "Kernel/OVR_Allocator.h" nuclear@0: #include "OVR_Stereo.h" nuclear@0: nuclear@0: #ifdef OVR_OS_WIN32 nuclear@0: #define WIN32_LEAN_AND_MEAN nuclear@0: #include nuclear@0: #include nuclear@0: #elif defined(OVR_OS_MS) // Other Microsoft OSs nuclear@0: // Nothing, thanks. nuclear@0: #else nuclear@0: #include nuclear@0: #include nuclear@0: nuclear@0: #ifdef OVR_OS_LINUX nuclear@0: #include nuclear@0: #include nuclear@0: #endif nuclear@0: nuclear@0: #endif nuclear@0: nuclear@0: #define PROFILE_VERSION 2.0 nuclear@0: #define MAX_PROFILE_MAJOR_VERSION 2 nuclear@0: #define MAX_DEVICE_PROFILE_MAJOR_VERSION 1 nuclear@0: nuclear@0: nuclear@0: namespace OVR { nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // ProfileDeviceKey nuclear@0: nuclear@0: ProfileDeviceKey::ProfileDeviceKey(const HMDInfo* info) : nuclear@0: Valid(false) nuclear@0: { nuclear@0: if (info) nuclear@0: { nuclear@0: PrintedSerial = info->PrintedSerial; nuclear@0: ProductName = SanitizeProductName(info->ProductName); nuclear@0: ProductId = info->ProductId; nuclear@0: HmdType = info->HmdType; nuclear@0: nuclear@0: if (ProductId != 0) nuclear@0: { nuclear@0: Valid = true; nuclear@0: } nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: ProductId = 0; nuclear@0: HmdType = HmdType_None; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: String ProfileDeviceKey::SanitizeProductName(String productName) nuclear@0: { nuclear@0: String result; nuclear@0: nuclear@0: if (!productName.IsEmpty()) nuclear@0: { nuclear@0: const char* product_name = productName.ToCStr(); nuclear@0: nuclear@0: // First strip off "Oculus" nuclear@0: const char* oculus = strstr(product_name, "Oculus "); nuclear@0: if (oculus) nuclear@0: { nuclear@0: product_name = oculus + OVR_strlen("Oculus "); nuclear@0: } nuclear@0: nuclear@0: // And remove spaces from the name nuclear@0: for (const char* s = product_name; *s != 0; s++) nuclear@0: { nuclear@0: if (*s != ' ') nuclear@0: { nuclear@0: result.AppendChar(*s); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return result; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // Returns the pathname of the JSON file containing the stored profiles nuclear@0: String GetBaseOVRPath(bool create_dir) nuclear@0: { nuclear@0: String path; nuclear@0: nuclear@0: #if defined(OVR_OS_WIN32) nuclear@0: nuclear@0: TCHAR data_path[MAX_PATH]; nuclear@0: SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, NULL, 0, data_path); nuclear@0: path = String(data_path); nuclear@0: nuclear@0: path += "/Oculus"; nuclear@0: nuclear@0: if (create_dir) nuclear@0: { // Create the Oculus directory if it doesn't exist nuclear@0: WCHAR wpath[128]; nuclear@0: OVR::UTF8Util::DecodeString(wpath, path.ToCStr()); nuclear@0: nuclear@0: DWORD attrib = GetFileAttributes(wpath); nuclear@0: bool exists = attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY); nuclear@0: if (!exists) nuclear@0: { nuclear@0: CreateDirectory(wpath, NULL); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: #elif defined(OVR_OS_OS) // Other Microsoft OSs nuclear@0: nuclear@0: // TODO: figure this out. nuclear@0: OVR_UNUSED ( create_dir ); nuclear@0: path = ""; nuclear@0: nuclear@0: #elif defined(OVR_OS_MAC) nuclear@0: nuclear@0: const char* home = getenv("HOME"); nuclear@0: path = home; nuclear@0: path += "/Library/Preferences/Oculus"; nuclear@0: nuclear@0: if (create_dir) nuclear@0: { // Create the Oculus directory if it doesn't exist nuclear@0: DIR* dir = opendir(path); nuclear@0: if (dir == NULL) nuclear@0: { nuclear@0: mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: closedir(dir); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: #else nuclear@0: nuclear@0: const char* home = getenv("HOME"); nuclear@0: path = home; nuclear@0: path += "/.config/Oculus"; nuclear@0: nuclear@0: if (create_dir) nuclear@0: { // Create the Oculus directory if it doesn't exist nuclear@0: DIR* dir = opendir(path); nuclear@0: if (dir == NULL) nuclear@0: { nuclear@0: mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: closedir(dir); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: #endif nuclear@0: nuclear@0: return path; nuclear@0: } nuclear@0: nuclear@0: String ProfileManager::GetProfilePath() nuclear@0: { nuclear@0: return BasePath + "/ProfileDB.json"; nuclear@0: } nuclear@0: nuclear@0: static JSON* FindTaggedData(JSON* data, const char** tag_names, const char** qtags, int num_qtags) nuclear@0: { nuclear@0: if (data == NULL || !(data->Name == "TaggedData") || data->Type != JSON_Array) nuclear@0: return NULL; nuclear@0: nuclear@0: JSON* tagged_item = data->GetFirstItem(); nuclear@0: while (tagged_item) nuclear@0: { nuclear@0: JSON* tags = tagged_item->GetItemByName("tags"); nuclear@0: if (tags->Type == JSON_Array && num_qtags == tags->GetArraySize()) nuclear@0: { // Check for a full tag match on each item nuclear@0: int num_matches = 0; nuclear@0: nuclear@0: for (int k=0; kGetFirstItem(); nuclear@0: while (tag) nuclear@0: { nuclear@0: JSON* tagval = tag->GetFirstItem(); nuclear@0: if (tagval && tagval->Name == tag_names[k]) nuclear@0: { nuclear@0: if (tagval->Value == qtags[k]) nuclear@0: num_matches++; nuclear@0: break; nuclear@0: } nuclear@0: tag = tags->GetNextItem(tag); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // if all tags were matched then copy the values into this Profile nuclear@0: if (num_matches == num_qtags) nuclear@0: { nuclear@0: JSON* vals = tagged_item->GetItemByName("vals"); nuclear@0: return vals; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: tagged_item = data->GetNextItem(tagged_item); nuclear@0: } nuclear@0: nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: static void FilterTaggedData(JSON* data, const char* tag_name, const char* qtag, Array& items) nuclear@0: { nuclear@0: if (data == NULL || !(data->Name == "TaggedData") || data->Type != JSON_Array) nuclear@0: return; nuclear@0: nuclear@0: JSON* tagged_item = data->GetFirstItem(); nuclear@0: while (tagged_item) nuclear@0: { nuclear@0: JSON* tags = tagged_item->GetItemByName("tags"); nuclear@0: if (tags->Type == JSON_Array) nuclear@0: { // Check for a tag match on the requested tag nuclear@0: nuclear@0: JSON* tag = tags->GetFirstItem(); nuclear@0: while (tag) nuclear@0: { nuclear@0: JSON* tagval = tag->GetFirstItem(); nuclear@0: if (tagval && tagval->Name == tag_name) nuclear@0: { nuclear@0: if (tagval->Value == qtag) nuclear@0: { // Add this item to the output list nuclear@0: items.PushBack(tagged_item); nuclear@0: } nuclear@0: break; nuclear@0: } nuclear@0: tag = tags->GetNextItem(tag); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: tagged_item = data->GetNextItem(tagged_item); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // ***** ProfileManager nuclear@0: nuclear@0: template<> ProfileManager* OVR::SystemSingletonBase::SlowGetInstance() nuclear@0: { nuclear@0: static OVR::Lock lock; nuclear@0: OVR::Lock::Locker locker(&lock); nuclear@0: if (!SingletonInstance) SingletonInstance = new ProfileManager(true); nuclear@0: return SingletonInstance; nuclear@0: } nuclear@0: nuclear@0: ProfileManager::ProfileManager(bool sys_register) : nuclear@0: Changed(false) nuclear@0: { nuclear@0: // Attempt to get the base path automatically, but this may fail nuclear@0: BasePath = GetBaseOVRPath(false); nuclear@0: nuclear@0: if (sys_register) nuclear@0: PushDestroyCallbacks(); nuclear@0: } nuclear@0: nuclear@0: ProfileManager::~ProfileManager() nuclear@0: { nuclear@0: ClearProfileData(); nuclear@0: } nuclear@0: nuclear@0: void ProfileManager::OnSystemDestroy() nuclear@0: { nuclear@0: delete this; nuclear@0: } nuclear@0: nuclear@0: // In the service process it is important to set the base path because this cannot be detected automatically nuclear@0: void ProfileManager::SetBasePath(String basePath) nuclear@0: { nuclear@0: if (basePath != BasePath) nuclear@0: { nuclear@0: BasePath = basePath; nuclear@0: LoadCache(false); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Clear the local profile cache nuclear@0: void ProfileManager::ClearProfileData() nuclear@0: { nuclear@0: Lock::Locker lockScope(&ProfileLock); nuclear@0: nuclear@0: ProfileCache.Clear(); nuclear@0: Changed = false; nuclear@0: } nuclear@0: nuclear@0: // Serializes the profiles to disk. nuclear@0: void ProfileManager::Save() nuclear@0: { nuclear@0: Lock::Locker lockScope(&ProfileLock); nuclear@0: nuclear@0: if (ProfileCache == NULL) nuclear@0: return; nuclear@0: nuclear@0: // Save the profile to disk nuclear@0: BasePath = GetBaseOVRPath(true); // create the base directory if it doesn't exist nuclear@0: String path = GetProfilePath(); nuclear@0: ProfileCache->Save(path); nuclear@0: Changed = false; nuclear@0: } nuclear@0: nuclear@0: // Returns a profile with all system default values nuclear@0: Profile* ProfileManager::GetDefaultProfile(HmdTypeEnum device) nuclear@0: { nuclear@0: // In the absence of any data, set some reasonable profile defaults. nuclear@0: // However, this is not future proof and developers should still nuclear@0: // provide reasonable default values for queried fields. nuclear@0: nuclear@0: // Biometric data nuclear@0: Profile* profile = CreateProfile(); nuclear@0: profile->SetValue(OVR_KEY_USER, "default"); nuclear@0: profile->SetValue(OVR_KEY_NAME, "Default"); nuclear@0: profile->SetValue(OVR_KEY_GENDER, OVR_DEFAULT_GENDER); nuclear@0: profile->SetFloatValue(OVR_KEY_PLAYER_HEIGHT, OVR_DEFAULT_PLAYER_HEIGHT); nuclear@0: profile->SetFloatValue(OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT); nuclear@0: profile->SetFloatValue(OVR_KEY_IPD, OVR_DEFAULT_IPD); nuclear@0: float half_ipd[2] = { OVR_DEFAULT_IPD / 2, OVR_DEFAULT_IPD / 2 }; nuclear@0: profile->SetFloatValues(OVR_KEY_EYE_TO_NOSE_DISTANCE, half_ipd, 2); nuclear@0: float dist[2] = {OVR_DEFAULT_NECK_TO_EYE_HORIZONTAL, OVR_DEFAULT_NECK_TO_EYE_VERTICAL}; nuclear@0: profile->SetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, dist, 2); nuclear@0: nuclear@0: // Device specific data nuclear@0: if (device != HmdType_None) nuclear@0: { nuclear@0: if (device == HmdType_CrystalCoveProto || device == HmdType_DK2) nuclear@0: { nuclear@0: profile->SetValue("EyeCup", "A"); nuclear@0: profile->SetIntValue(OVR_KEY_EYE_RELIEF_DIAL, OVR_DEFAULT_EYE_RELIEF_DIAL); nuclear@0: nuclear@0: // TODO: These defaults are a little bogus and designed for continuity with 0.3 nuclear@0: // eye-relief values. We need better measurement-based numbers in future releases nuclear@0: float max_eye_plate[2] = { 0.01965f + 0.018f, 0.01965f + 0.018f }; nuclear@0: profile->SetFloatValues(OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE, max_eye_plate, 2); nuclear@0: } nuclear@0: else nuclear@0: { // DK1 and DKHD variants nuclear@0: profile->SetValue("EyeCup", "A"); nuclear@0: profile->SetIntValue(OVR_KEY_EYE_RELIEF_DIAL, OVR_DEFAULT_EYE_RELIEF_DIAL); nuclear@0: nuclear@0: // TODO: These defaults are a little bogus and designed for continuity with 0.3 nuclear@0: // DK1 distortion. We need better measurement-based numbers in future releases nuclear@0: float max_eye_plate[2] = { 0.02357f + 0.017f, 0.02357f + 0.017f }; nuclear@0: profile->SetFloatValues(OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE, max_eye_plate, 2); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return profile; nuclear@0: } nuclear@0: nuclear@0: //------------------------------------------------------------------------------ nuclear@0: void ProfileManager::Read() nuclear@0: { nuclear@0: LoadCache(false); nuclear@0: } nuclear@0: nuclear@0: // Populates the local profile cache. This occurs on the first access of the profile nuclear@0: // data. All profile operations are performed against the local cache until the nuclear@0: // ProfileManager is released or goes out of scope at which time the cache is serialized nuclear@0: // to disk. nuclear@0: void ProfileManager::LoadCache(bool create) nuclear@0: { nuclear@0: Lock::Locker lockScope(&ProfileLock); nuclear@0: nuclear@0: ClearProfileData(); nuclear@0: nuclear@0: String path = GetProfilePath(); nuclear@0: nuclear@0: Ptr root = *JSON::Load(path); nuclear@0: if (root == NULL) nuclear@0: { nuclear@0: path = BasePath + "/Profiles.json"; // look for legacy profile nuclear@0: root = *JSON::Load(path); nuclear@0: nuclear@0: if (root == NULL) nuclear@0: { nuclear@0: if (create) nuclear@0: { // Generate a skeleton profile database nuclear@0: root = *JSON::CreateObject(); nuclear@0: root->AddNumberItem("Oculus Profile Version", 2.0); nuclear@0: root->AddItem("Users", JSON::CreateArray()); nuclear@0: root->AddItem("TaggedData", JSON::CreateArray()); nuclear@0: ProfileCache = root; nuclear@0: } nuclear@0: nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: // Verify the legacy version nuclear@0: JSON* version_item = root->GetFirstItem(); nuclear@0: if (version_item->Name == "Oculus Profile Version") nuclear@0: { nuclear@0: int major = atoi(version_item->Value.ToCStr()); nuclear@0: if (major != 1) nuclear@0: return; // don't use the file on unsupported major version number nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: return; // invalid file nuclear@0: } nuclear@0: nuclear@0: // Convert the legacy format to the new database format nuclear@0: LoadV1Profiles(root); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // Verify the file format and version nuclear@0: JSON* version_item = root->GetFirstItem(); nuclear@0: if (version_item && version_item->Name == "Oculus Profile Version") nuclear@0: { nuclear@0: int major = atoi(version_item->Value.ToCStr()); nuclear@0: if (major != 2) nuclear@0: return; // don't use the file on unsupported major version number nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: return; // invalid file nuclear@0: } nuclear@0: nuclear@0: ProfileCache = root; // store the database contents for traversal nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: void ProfileManager::LoadV1Profiles(JSON* v1) nuclear@0: { nuclear@0: JSON* item0 = v1->GetFirstItem(); nuclear@0: JSON* item1 = v1->GetNextItem(item0); nuclear@0: JSON* item2 = v1->GetNextItem(item1); nuclear@0: nuclear@0: OVR_ASSERT(item1 && item2); nuclear@0: if(!item1 || !item2) nuclear@0: return; nuclear@0: nuclear@0: // Create the new profile database nuclear@0: Ptr root = *JSON::CreateObject(); nuclear@0: root->AddNumberItem("Oculus Profile Version", 2.0); nuclear@0: root->AddItem("Users", JSON::CreateArray()); nuclear@0: root->AddItem("TaggedData", JSON::CreateArray()); nuclear@0: ProfileCache = root; nuclear@0: nuclear@0: const char* default_dk1_user = item1->Value; nuclear@0: nuclear@0: // Read the number of profiles nuclear@0: int profileCount = (int)item2->dValue; nuclear@0: JSON* profileItem = item2; nuclear@0: nuclear@0: for (int p=0; pGetNextItem(profileItem); nuclear@0: if (profileItem == NULL) nuclear@0: break; nuclear@0: nuclear@0: if (profileItem->Name == "Profile") nuclear@0: { nuclear@0: // Read the required Name field nuclear@0: const char* profileName; nuclear@0: JSON* item = profileItem->GetFirstItem(); nuclear@0: nuclear@0: if (item && (item->Name == "Name")) nuclear@0: { nuclear@0: profileName = item->Value; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: return; // invalid field nuclear@0: } nuclear@0: nuclear@0: // Read the user profile fields nuclear@0: if (CreateUser(profileName, profileName)) nuclear@0: { nuclear@0: const char* tag_names[2] = {"User", "Product"}; nuclear@0: const char* tags[2]; nuclear@0: tags[0] = profileName; nuclear@0: nuclear@0: Ptr user_profile = *CreateProfile(); nuclear@0: user_profile->SetValue(OVR_KEY_NAME, profileName); nuclear@0: nuclear@0: float neckeye[2] = { 0, 0 }; nuclear@0: nuclear@0: item = profileItem->GetNextItem(item); nuclear@0: while (item) nuclear@0: { nuclear@0: if (item->Type != JSON_Object) nuclear@0: { nuclear@0: if (item->Name == OVR_KEY_PLAYER_HEIGHT) nuclear@0: { // Add an explicit eye height nuclear@0: nuclear@0: } nuclear@0: if (item->Name == "NeckEyeHori") nuclear@0: neckeye[0] = (float)item->dValue; nuclear@0: else if (item->Name == "NeckEyeVert") nuclear@0: neckeye[1] = (float)item->dValue; nuclear@0: else nuclear@0: user_profile->SetValue(item); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // Add the user/device tag values nuclear@0: const char* device_name = item->Name.ToCStr(); nuclear@0: Ptr device_profile = *CreateProfile(); nuclear@0: nuclear@0: JSON* device_item = item->GetFirstItem(); nuclear@0: while (device_item) nuclear@0: { nuclear@0: device_profile->SetValue(device_item); nuclear@0: device_item = item->GetNextItem(device_item); nuclear@0: } nuclear@0: nuclear@0: tags[1] = device_name; nuclear@0: SetTaggedProfile(tag_names, tags, 2, device_profile); nuclear@0: } nuclear@0: nuclear@0: item = profileItem->GetNextItem(item); nuclear@0: } nuclear@0: nuclear@0: // Add an explicit eye-height field nuclear@0: float player_height = user_profile->GetFloatValue(OVR_KEY_PLAYER_HEIGHT, nuclear@0: OVR_DEFAULT_PLAYER_HEIGHT); nuclear@0: if (player_height > 0) nuclear@0: { nuclear@0: char gender[16]; nuclear@0: user_profile->GetValue(OVR_KEY_GENDER, gender, 16); nuclear@0: nuclear@0: const float EYE_TO_HEADTOP_RATIO = 0.44538f; nuclear@0: const float MALE_AVG_HEAD_HEIGHT = 0.232f; nuclear@0: const float FEMALE_AVG_HEAD_HEIGHT = 0.218f; nuclear@0: nuclear@0: // compute distance from top of skull to the eye nuclear@0: float head_height; nuclear@0: if (OVR_strcmp(gender, "Female") == 0) nuclear@0: head_height = FEMALE_AVG_HEAD_HEIGHT; nuclear@0: else nuclear@0: head_height = MALE_AVG_HEAD_HEIGHT; nuclear@0: nuclear@0: float skull = EYE_TO_HEADTOP_RATIO * head_height; nuclear@0: float eye_height = player_height - skull; nuclear@0: nuclear@0: user_profile->SetFloatValue(OVR_KEY_EYE_HEIGHT, eye_height); nuclear@0: } nuclear@0: nuclear@0: // Convert NeckEye values to an array nuclear@0: if (neckeye[0] > 0 && neckeye[1] > 0) nuclear@0: user_profile->SetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, neckeye, 2); nuclear@0: nuclear@0: // Add the user tag values nuclear@0: SetTaggedProfile(tag_names, tags, 1, user_profile); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // since V1 profiles were only for DK1, the assign the user to all DK1's nuclear@0: const char* tag_names[1] = { "Product" }; nuclear@0: const char* tags[1] = { "RiftDK1" }; nuclear@0: Ptr product_profile = *CreateProfile(); nuclear@0: product_profile->SetValue("DefaultUser", default_dk1_user); nuclear@0: SetTaggedProfile(tag_names, tags, 1, product_profile); nuclear@0: } nuclear@0: nuclear@0: // Returns the number of stored profiles for this device type nuclear@0: int ProfileManager::GetUserCount() nuclear@0: { nuclear@0: Lock::Locker lockScope(&ProfileLock); nuclear@0: nuclear@0: if (ProfileCache == NULL) nuclear@0: { // Load the cache nuclear@0: LoadCache(false); nuclear@0: if (ProfileCache == NULL) nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: JSON* users = ProfileCache->GetItemByName("Users"); nuclear@0: if (users == NULL) nuclear@0: return 0; nuclear@0: nuclear@0: return users->GetItemCount(); nuclear@0: } nuclear@0: nuclear@0: bool ProfileManager::CreateUser(const char* user, const char* name) nuclear@0: { nuclear@0: Lock::Locker lockScope(&ProfileLock); nuclear@0: nuclear@0: if (ProfileCache == NULL) nuclear@0: { // Load the cache nuclear@0: LoadCache(true); nuclear@0: if (ProfileCache == NULL) nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: JSON* users = ProfileCache->GetItemByName("Users"); nuclear@0: if (users == NULL) nuclear@0: { // Generate the User section nuclear@0: users = JSON::CreateArray(); nuclear@0: ProfileCache->AddItem("Users", users); nuclear@0: //TODO: Insert this before the TaggedData nuclear@0: } nuclear@0: nuclear@0: // Search for the pre-existence of this user nuclear@0: JSON* user_item = users->GetFirstItem(); nuclear@0: int index = 0; nuclear@0: while (user_item) nuclear@0: { nuclear@0: JSON* userid = user_item->GetItemByName("User"); nuclear@0: int compare = OVR_strcmp(user, userid->Value); nuclear@0: if (compare == 0) nuclear@0: { // The user already exists so simply update the fields nuclear@0: JSON* name_item = user_item->GetItemByName("Name"); nuclear@0: if (name_item && OVR_strcmp(name, name_item->Value) != 0) nuclear@0: { nuclear@0: name_item->Value = name; nuclear@0: Changed = true; nuclear@0: } nuclear@0: return true; nuclear@0: } nuclear@0: else if (compare < 0) nuclear@0: { // A new user should be placed before this item nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: user_item = users->GetNextItem(user_item); nuclear@0: index++; nuclear@0: } nuclear@0: nuclear@0: // Create and fill the user struct nuclear@0: JSON* new_user = JSON::CreateObject(); nuclear@0: new_user->AddStringItem(OVR_KEY_USER, user); nuclear@0: new_user->AddStringItem(OVR_KEY_NAME, name); nuclear@0: // user_item->AddStringItem("Password", password); nuclear@0: nuclear@0: if (user_item == NULL) nuclear@0: users->AddArrayElement(new_user); nuclear@0: else nuclear@0: users->InsertArrayElement(index, new_user); nuclear@0: nuclear@0: Changed = true; nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: bool ProfileManager::HasUser(const char* user) nuclear@0: { nuclear@0: Lock::Locker lockScope(&ProfileLock); nuclear@0: nuclear@0: if (ProfileCache == NULL) nuclear@0: { // Load the cache nuclear@0: LoadCache(false); nuclear@0: if (ProfileCache == NULL) nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: JSON* users = ProfileCache->GetItemByName("Users"); nuclear@0: if (users == NULL) nuclear@0: return false; nuclear@0: nuclear@0: // Remove this user from the User table nuclear@0: JSON* user_item = users->GetFirstItem(); nuclear@0: while (user_item) nuclear@0: { nuclear@0: JSON* userid = user_item->GetItemByName("User"); nuclear@0: if (OVR_strcmp(user, userid->Value) == 0) nuclear@0: { nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: user_item = users->GetNextItem(user_item); nuclear@0: } nuclear@0: nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: // Returns the user id of a specific user in the list. The returned nuclear@0: // memory is locally allocated and should not be stored or deleted. Returns NULL nuclear@0: // if the index is invalid nuclear@0: const char* ProfileManager::GetUser(unsigned int index) nuclear@0: { nuclear@0: Lock::Locker lockScope(&ProfileLock); nuclear@0: nuclear@0: if (ProfileCache == NULL) nuclear@0: { // Load the cache nuclear@0: LoadCache(false); nuclear@0: if (ProfileCache == NULL) nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: JSON* users = ProfileCache->GetItemByName("Users"); nuclear@0: nuclear@0: if (users && index < users->GetItemCount()) nuclear@0: { nuclear@0: JSON* user_item = users->GetItemByIndex(index); nuclear@0: if (user_item) nuclear@0: { nuclear@0: JSON* user = user_item->GetFirstItem(); nuclear@0: if (user) nuclear@0: { nuclear@0: JSON* userid = user_item->GetItemByName(OVR_KEY_USER); nuclear@0: if (userid) nuclear@0: return userid->Value.ToCStr(); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: bool ProfileManager::RemoveUser(const char* user) nuclear@0: { nuclear@0: Lock::Locker lockScope(&ProfileLock); nuclear@0: nuclear@0: if (ProfileCache == NULL) nuclear@0: { // Load the cache nuclear@0: LoadCache(false); nuclear@0: if (ProfileCache == NULL) nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: JSON* users = ProfileCache->GetItemByName("Users"); nuclear@0: if (users == NULL) nuclear@0: return true; nuclear@0: nuclear@0: // Remove this user from the User table nuclear@0: JSON* user_item = users->GetFirstItem(); nuclear@0: while (user_item) nuclear@0: { nuclear@0: JSON* userid = user_item->GetItemByName("User"); nuclear@0: if (OVR_strcmp(user, userid->Value) == 0) nuclear@0: { // Delete the user entry nuclear@0: user_item->RemoveNode(); nuclear@0: user_item->Release(); nuclear@0: Changed = true; nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: user_item = users->GetNextItem(user_item); nuclear@0: } nuclear@0: nuclear@0: // Now remove all data entries with this user tag nuclear@0: JSON* tagged_data = ProfileCache->GetItemByName("TaggedData"); nuclear@0: Array user_items; nuclear@0: FilterTaggedData(tagged_data, "User", user, user_items); nuclear@0: for (unsigned int i=0; iRemoveNode(); nuclear@0: user_items[i]->Release(); nuclear@0: Changed = true; nuclear@0: } nuclear@0: nuclear@0: return Changed; nuclear@0: } nuclear@0: nuclear@0: Profile* ProfileManager::CreateProfile() nuclear@0: { nuclear@0: Profile* profile = new Profile(BasePath); nuclear@0: return profile; nuclear@0: } nuclear@0: nuclear@0: const char* ProfileManager::GetDefaultUser(const ProfileDeviceKey& deviceKey) nuclear@0: { nuclear@0: const char* product_str = deviceKey.ProductName.IsEmpty() ? NULL : deviceKey.ProductName.ToCStr(); nuclear@0: const char* serial_str = deviceKey.PrintedSerial.IsEmpty() ? NULL : deviceKey.PrintedSerial.ToCStr(); nuclear@0: nuclear@0: return GetDefaultUser(product_str, serial_str); nuclear@0: } nuclear@0: nuclear@0: // Returns the name of the profile that is marked as the current default user. nuclear@0: const char* ProfileManager::GetDefaultUser(const char* product, const char* serial) nuclear@0: { nuclear@0: const char* tag_names[2] = {"Product", "Serial"}; nuclear@0: const char* tags[2]; nuclear@0: nuclear@0: if (product && serial) nuclear@0: { nuclear@0: tags[0] = product; nuclear@0: tags[1] = serial; nuclear@0: // Look for a default user on this specific device nuclear@0: Ptr p = *GetTaggedProfile(tag_names, tags, 2); nuclear@0: if (p == NULL) nuclear@0: { // Look for a default user on this product nuclear@0: p = *GetTaggedProfile(tag_names, tags, 1); nuclear@0: } nuclear@0: nuclear@0: if (p) nuclear@0: { nuclear@0: const char* user = p->GetValue("DefaultUser"); nuclear@0: if (user != NULL && user[0] != 0) nuclear@0: { nuclear@0: TempBuff = user; nuclear@0: return TempBuff.ToCStr(); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: bool ProfileManager::SetDefaultUser(const ProfileDeviceKey& deviceKey, const char* user) nuclear@0: { nuclear@0: const char* tag_names[2] = {"Product", "Serial"}; nuclear@0: const char* tags[2]; nuclear@0: nuclear@0: const char* product_str = deviceKey.ProductName.IsEmpty() ? NULL : deviceKey.ProductName.ToCStr(); nuclear@0: const char* serial_str = deviceKey.PrintedSerial.IsEmpty() ? NULL : deviceKey.PrintedSerial.ToCStr(); nuclear@0: nuclear@0: if (product_str && serial_str) nuclear@0: { nuclear@0: tags[0] = product_str; nuclear@0: tags[1] = serial_str; nuclear@0: nuclear@0: Ptr p = *CreateProfile(); nuclear@0: p->SetValue("DefaultUser", user); nuclear@0: return SetTaggedProfile(tag_names, tags, 2, p); nuclear@0: } nuclear@0: nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: Profile* ProfileManager::GetTaggedProfile(const char** tag_names, const char** tags, int num_tags) nuclear@0: { nuclear@0: Lock::Locker lockScope(&ProfileLock); nuclear@0: nuclear@0: if (ProfileCache == NULL) nuclear@0: { // Load the cache nuclear@0: LoadCache(false); nuclear@0: if (ProfileCache == NULL) nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: JSON* tagged_data = ProfileCache->GetItemByName("TaggedData"); nuclear@0: OVR_ASSERT(tagged_data); nuclear@0: if (tagged_data == NULL) nuclear@0: return NULL; nuclear@0: nuclear@0: Profile* profile = new Profile(BasePath); nuclear@0: nuclear@0: JSON* vals = FindTaggedData(tagged_data, tag_names, tags, num_tags); nuclear@0: if (vals) nuclear@0: { nuclear@0: JSON* item = vals->GetFirstItem(); nuclear@0: while (item) nuclear@0: { nuclear@0: //printf("Add %s, %s\n", item->Name.ToCStr(), item->Value.ToCStr()); nuclear@0: //profile->Settings.Set(item->Name, item->Value); nuclear@0: profile->SetValue(item); nuclear@0: item = vals->GetNextItem(item); nuclear@0: } nuclear@0: nuclear@0: return profile; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: profile->Release(); nuclear@0: return NULL; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: bool ProfileManager::SetTaggedProfile(const char** tag_names, const char** tags, int num_tags, Profile* profile) nuclear@0: { nuclear@0: Lock::Locker lockScope(&ProfileLock); nuclear@0: nuclear@0: if (ProfileCache == NULL) nuclear@0: { // Load the cache nuclear@0: LoadCache(true); nuclear@0: if (ProfileCache == NULL) nuclear@0: return false; // TODO: Generate a new profile DB nuclear@0: } nuclear@0: nuclear@0: JSON* tagged_data = ProfileCache->GetItemByName("TaggedData"); nuclear@0: OVR_ASSERT(tagged_data); nuclear@0: if (tagged_data == NULL) nuclear@0: return false; nuclear@0: nuclear@0: // Get the cached tagged data section nuclear@0: JSON* vals = FindTaggedData(tagged_data, tag_names, tags, num_tags); nuclear@0: if (vals == NULL) nuclear@0: { nuclear@0: JSON* tagged_item = JSON::CreateObject(); nuclear@0: JSON* taglist = JSON::CreateArray(); nuclear@0: for (int i=0; iAddStringItem(tag_names[i], tags[i]); nuclear@0: taglist->AddArrayElement(k); nuclear@0: } nuclear@0: nuclear@0: vals = JSON::CreateObject(); nuclear@0: nuclear@0: tagged_item->AddItem("tags", taglist); nuclear@0: tagged_item->AddItem("vals", vals); nuclear@0: tagged_data->AddArrayElement(tagged_item); nuclear@0: } nuclear@0: nuclear@0: // Now add or update each profile setting in cache nuclear@0: for (unsigned int i=0; iValues.GetSize(); i++) nuclear@0: { nuclear@0: JSON* value = profile->Values[i]; nuclear@0: nuclear@0: bool found = false; nuclear@0: JSON* item = vals->GetFirstItem(); nuclear@0: while (item) nuclear@0: { nuclear@0: if (value->Name == item->Name) nuclear@0: { nuclear@0: // Don't allow a pre-existing type to be overridden nuclear@0: OVR_ASSERT(value->Type == item->Type); nuclear@0: nuclear@0: if (value->Type == item->Type) nuclear@0: { // Check for the same value nuclear@0: if (value->Type == JSON_Array) nuclear@0: { // Update each array item nuclear@0: if (item->GetArraySize() == value->GetArraySize()) nuclear@0: { // Update each value (assumed to be basic types and not array of objects) nuclear@0: JSON* value_element = value->GetFirstItem(); nuclear@0: JSON* item_element = item->GetFirstItem(); nuclear@0: while (item_element && value_element) nuclear@0: { nuclear@0: if (value_element->Type == JSON_String) nuclear@0: { nuclear@0: if (item_element->Value != value_element->Value) nuclear@0: { // Overwrite the changed value and mark for file update nuclear@0: item_element->Value = value_element->Value; nuclear@0: Changed = true; nuclear@0: } nuclear@0: } nuclear@0: else { nuclear@0: if (item_element->dValue != value_element->dValue) nuclear@0: { // Overwrite the changed value and mark for file update nuclear@0: item_element->dValue = value_element->dValue; nuclear@0: Changed = true; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: value_element = value->GetNextItem(value_element); nuclear@0: item_element = item->GetNextItem(item_element); nuclear@0: } nuclear@0: } nuclear@0: else nuclear@0: { // if the array size changed, simply create a new one nuclear@0: // TODO: Create the new array nuclear@0: } nuclear@0: } nuclear@0: else if (value->Type == JSON_String) nuclear@0: { nuclear@0: if (item->Value != value->Value) nuclear@0: { // Overwrite the changed value and mark for file update nuclear@0: item->Value = value->Value; nuclear@0: Changed = true; nuclear@0: } nuclear@0: } nuclear@0: else { nuclear@0: if (item->dValue != value->dValue) nuclear@0: { // Overwrite the changed value and mark for file update nuclear@0: item->dValue = value->dValue; nuclear@0: Changed = true; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: found = true; nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: item = vals->GetNextItem(item); nuclear@0: } nuclear@0: nuclear@0: if (!found) nuclear@0: { // Add the new value nuclear@0: Changed = true; nuclear@0: nuclear@0: if (value->Type == JSON_String) nuclear@0: vals->AddStringItem(value->Name, value->Value); nuclear@0: else if (value->Type == JSON_Bool) nuclear@0: vals->AddBoolItem(value->Name, ((int)value->dValue != 0)); nuclear@0: else if (value->Type == JSON_Number) nuclear@0: vals->AddNumberItem(value->Name, value->dValue); nuclear@0: else if (value->Type == JSON_Array) nuclear@0: vals->AddItem(value->Name, value->Copy()); nuclear@0: else nuclear@0: { nuclear@0: OVR_ASSERT(false); nuclear@0: Changed = false; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: Profile* ProfileManager::GetDefaultUserProfile(const ProfileDeviceKey& deviceKey) nuclear@0: { nuclear@0: const char* userName = GetDefaultUser(deviceKey); nuclear@0: nuclear@0: Profile* profile = GetProfile(deviceKey, userName); nuclear@0: nuclear@0: if (!profile) nuclear@0: { nuclear@0: profile = GetDefaultProfile(deviceKey.HmdType); nuclear@0: } nuclear@0: nuclear@0: return profile; nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: Profile* ProfileManager::GetProfile(const ProfileDeviceKey& deviceKey, const char* user) nuclear@0: { nuclear@0: Lock::Locker lockScope(&ProfileLock); nuclear@0: nuclear@0: if (ProfileCache == NULL) nuclear@0: { // Load the cache nuclear@0: LoadCache(false); nuclear@0: if (ProfileCache == NULL) nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: Profile* profile = new Profile(BasePath); nuclear@0: nuclear@0: if (deviceKey.Valid) nuclear@0: { nuclear@0: if (!profile->LoadDeviceProfile(deviceKey) && (user == NULL)) nuclear@0: { nuclear@0: profile->Release(); nuclear@0: return NULL; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (user) nuclear@0: { nuclear@0: const char* product_str = deviceKey.ProductName.IsEmpty() ? NULL : deviceKey.ProductName.ToCStr(); nuclear@0: const char* serial_str = deviceKey.PrintedSerial.IsEmpty() ? NULL : deviceKey.PrintedSerial.ToCStr(); nuclear@0: nuclear@0: if (!profile->LoadProfile(ProfileCache.GetPtr(), user, product_str, serial_str)) nuclear@0: { nuclear@0: profile->Release(); nuclear@0: return NULL; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return profile; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // ***** Profile nuclear@0: nuclear@0: Profile::~Profile() nuclear@0: { nuclear@0: ValMap.Clear(); nuclear@0: for (unsigned int i=0; iRelease(); nuclear@0: nuclear@0: Values.Clear(); nuclear@0: } nuclear@0: nuclear@0: bool Profile::Close() nuclear@0: { nuclear@0: // TODO: nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: void Profile::CopyItems(JSON* root, String prefix) nuclear@0: { nuclear@0: JSON* item = root->GetFirstItem(); nuclear@0: while (item) nuclear@0: { nuclear@0: String item_name; nuclear@0: if (prefix.IsEmpty()) nuclear@0: item_name = item->Name; nuclear@0: else nuclear@0: item_name = prefix + "." + item->Name; nuclear@0: nuclear@0: if (item->Type == JSON_Object) nuclear@0: { // recursively copy the children nuclear@0: nuclear@0: CopyItems(item, item_name); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: //Settings.Set(item_name, item->Value); nuclear@0: SetValue(item); nuclear@0: } nuclear@0: nuclear@0: item = root->GetNextItem(item); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: bool Profile::LoadDeviceFile(unsigned int productId, const char* printedSerialNumber) nuclear@0: { nuclear@0: if (printedSerialNumber[0] == 0) nuclear@0: return false; nuclear@0: nuclear@0: String path = BasePath + "/Devices.json"; nuclear@0: nuclear@0: // Load the device profiles nuclear@0: Ptr root = *JSON::Load(path); nuclear@0: if (root == NULL) nuclear@0: return false; nuclear@0: nuclear@0: // Quick sanity check of the file type and format before we parse it nuclear@0: JSON* version = root->GetFirstItem(); nuclear@0: if (version && version->Name == "Oculus Device Profile Version") nuclear@0: { nuclear@0: int major = atoi(version->Value.ToCStr()); nuclear@0: if (major > MAX_DEVICE_PROFILE_MAJOR_VERSION) nuclear@0: return false; // don't parse the file on unsupported major version number nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: JSON* device = root->GetNextItem(version); nuclear@0: while (device) nuclear@0: { nuclear@0: if (device->Name == "Device") nuclear@0: { nuclear@0: JSON* product_item = device->GetItemByName("ProductID"); nuclear@0: JSON* serial_item = device->GetItemByName("Serial"); nuclear@0: if (product_item && serial_item && nuclear@0: (product_item->dValue == productId) && (serial_item->Value == printedSerialNumber)) nuclear@0: { nuclear@0: // found the entry for this device so recursively copy all the settings to the profile nuclear@0: CopyItems(device, ""); nuclear@0: return true; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: device = root->GetNextItem(device); nuclear@0: } nuclear@0: nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: #if 0 nuclear@0: //----------------------------------------------------------------------------- nuclear@0: static int BCDByte(unsigned int byte) nuclear@0: { nuclear@0: int digit1 = (byte >> 4) & 0x000f; nuclear@0: int digit2 = byte & 0x000f; nuclear@0: int decimal = digit1 * 10 + digit2; nuclear@0: return decimal; nuclear@0: } nuclear@0: #endif nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: bool Profile::LoadDeviceProfile(const ProfileDeviceKey& deviceKey) nuclear@0: { nuclear@0: bool success = false; nuclear@0: if (!deviceKey.Valid) nuclear@0: return false; nuclear@0: nuclear@0: #if 0 nuclear@0: int dev_major = BCDByte((sinfo.Version >> 8) & 0x00ff); nuclear@0: OVR_UNUSED(dev_major); nuclear@0: //int dev_minor = BCDByte(sinfo.Version & 0xff); nuclear@0: nuclear@0: //if (dev_minor > 18) nuclear@0: //{ // If the firmware supports hardware stored profiles then grab the device profile nuclear@0: // from the sensor nuclear@0: // TBD: Implement this nuclear@0: //} nuclear@0: //else nuclear@0: { nuclear@0: #endif nuclear@0: // Grab the model and serial number from the device and use it to access the device nuclear@0: // profile file stored on the local machine nuclear@0: success = LoadDeviceFile(deviceKey.ProductId, deviceKey.PrintedSerial); nuclear@0: //} nuclear@0: nuclear@0: return success; nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: bool Profile::LoadUser(JSON* root, nuclear@0: const char* user, nuclear@0: const char* model_name, nuclear@0: const char* device_serial) nuclear@0: { nuclear@0: if (user == NULL) nuclear@0: return false; nuclear@0: nuclear@0: // For legacy files, convert to old style names nuclear@0: //if (model_name && OVR_strcmp(model_name, "Oculus Rift DK1") == 0) nuclear@0: // model_name = "RiftDK1"; nuclear@0: nuclear@0: bool user_found = false; nuclear@0: JSON* data = root->GetItemByName("TaggedData"); nuclear@0: if (data) nuclear@0: { nuclear@0: const char* tag_names[3]; nuclear@0: const char* tags[3]; nuclear@0: tag_names[0] = "User"; nuclear@0: tags[0] = user; nuclear@0: int num_tags = 1; nuclear@0: nuclear@0: if (model_name) nuclear@0: { nuclear@0: tag_names[num_tags] = "Product"; nuclear@0: tags[num_tags] = model_name; nuclear@0: num_tags++; nuclear@0: } nuclear@0: nuclear@0: if (device_serial) nuclear@0: { nuclear@0: tag_names[num_tags] = "Serial"; nuclear@0: tags[num_tags] = device_serial; nuclear@0: num_tags++; nuclear@0: } nuclear@0: nuclear@0: // Retrieve all tag permutations nuclear@0: for (int combos=1; combos<=num_tags; combos++) nuclear@0: { nuclear@0: for (int i=0; i<(num_tags - combos + 1); i++) nuclear@0: { nuclear@0: JSON* vals = FindTaggedData(data, tag_names+i, tags+i, combos); nuclear@0: if (vals) nuclear@0: { nuclear@0: if (i==0) // This tag-combination contains a user match nuclear@0: user_found = true; nuclear@0: nuclear@0: // Add the values to the Profile. More specialized multi-tag values nuclear@0: // will take precedence over and overwrite generalized ones nuclear@0: // For example: ("Me","RiftDK1").IPD would overwrite ("Me").IPD nuclear@0: JSON* item = vals->GetFirstItem(); nuclear@0: while (item) nuclear@0: { nuclear@0: //printf("Add %s, %s\n", item->Name.ToCStr(), item->Value.ToCStr()); nuclear@0: //Settings.Set(item->Name, item->Value); nuclear@0: SetValue(item); nuclear@0: item = vals->GetNextItem(item); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (user_found) nuclear@0: SetValue(OVR_KEY_USER, user); nuclear@0: nuclear@0: return user_found; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: bool Profile::LoadProfile(JSON* root, nuclear@0: const char* user, nuclear@0: const char* device_model, nuclear@0: const char* device_serial) nuclear@0: { nuclear@0: if (!LoadUser(root, user, device_model, device_serial)) nuclear@0: return false; nuclear@0: nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: char* Profile::GetValue(const char* key, char* val, int val_length) const nuclear@0: { nuclear@0: JSON* value = NULL; nuclear@0: if (ValMap.Get(key, &value)) nuclear@0: { nuclear@0: OVR_strcpy(val, val_length, value->Value.ToCStr()); nuclear@0: return val; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: val[0] = 0; nuclear@0: return NULL; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: const char* Profile::GetValue(const char* key) nuclear@0: { nuclear@0: // Non-reentrant query. The returned buffer can only be used until the next call nuclear@0: // to GetValue() nuclear@0: JSON* value = NULL; nuclear@0: if (ValMap.Get(key, &value)) nuclear@0: { nuclear@0: TempVal = value->Value; nuclear@0: return TempVal.ToCStr(); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: return NULL; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: int Profile::GetNumValues(const char* key) const nuclear@0: { nuclear@0: JSON* value = NULL; nuclear@0: if (ValMap.Get(key, &value)) nuclear@0: { nuclear@0: if (value->Type == JSON_Array) nuclear@0: return value->GetArraySize(); nuclear@0: else nuclear@0: return 1; nuclear@0: } nuclear@0: else nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: bool Profile::GetBoolValue(const char* key, bool default_val) const nuclear@0: { nuclear@0: JSON* value = NULL; nuclear@0: if (ValMap.Get(key, &value) && value->Type == JSON_Bool) nuclear@0: return (value->dValue != 0); nuclear@0: else nuclear@0: return default_val; nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: int Profile::GetIntValue(const char* key, int default_val) const nuclear@0: { nuclear@0: JSON* value = NULL; nuclear@0: if (ValMap.Get(key, &value) && value->Type == JSON_Number) nuclear@0: return (int)(value->dValue); nuclear@0: else nuclear@0: return default_val; nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: float Profile::GetFloatValue(const char* key, float default_val) const nuclear@0: { nuclear@0: JSON* value = NULL; nuclear@0: if (ValMap.Get(key, &value) && value->Type == JSON_Number) nuclear@0: return (float)(value->dValue); nuclear@0: else nuclear@0: return default_val; nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: int Profile::GetFloatValues(const char* key, float* values, int num_vals) const nuclear@0: { nuclear@0: JSON* value = NULL; nuclear@0: if (ValMap.Get(key, &value) && value->Type == JSON_Array) nuclear@0: { nuclear@0: int val_count = Alg::Min(value->GetArraySize(), num_vals); nuclear@0: JSON* item = value->GetFirstItem(); nuclear@0: int count=0; nuclear@0: while (item && count < val_count) nuclear@0: { nuclear@0: if (item->Type == JSON_Number) nuclear@0: values[count] = (float)item->dValue; nuclear@0: else nuclear@0: break; nuclear@0: nuclear@0: count++; nuclear@0: item = value->GetNextItem(item); nuclear@0: } nuclear@0: nuclear@0: return count; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: return 0; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: double Profile::GetDoubleValue(const char* key, double default_val) const nuclear@0: { nuclear@0: JSON* value = NULL; nuclear@0: if (ValMap.Get(key, &value) && value->Type == JSON_Number) nuclear@0: return value->dValue; nuclear@0: else nuclear@0: return default_val; nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: int Profile::GetDoubleValues(const char* key, double* values, int num_vals) const nuclear@0: { nuclear@0: JSON* value = NULL; nuclear@0: if (ValMap.Get(key, &value) && value->Type == JSON_Array) nuclear@0: { nuclear@0: int val_count = Alg::Min(value->GetArraySize(), num_vals); nuclear@0: JSON* item = value->GetFirstItem(); nuclear@0: int count=0; nuclear@0: while (item && count < val_count) nuclear@0: { nuclear@0: if (item->Type == JSON_Number) nuclear@0: values[count] = item->dValue; nuclear@0: else nuclear@0: break; nuclear@0: nuclear@0: count++; nuclear@0: item = value->GetNextItem(item); nuclear@0: } nuclear@0: nuclear@0: return count; nuclear@0: } nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: void Profile::SetValue(JSON* val) nuclear@0: { nuclear@0: if (val == NULL) nuclear@0: return; nuclear@0: nuclear@0: if (val->Type == JSON_Number) nuclear@0: SetDoubleValue(val->Name, val->dValue); nuclear@0: else if (val->Type == JSON_Bool) nuclear@0: SetBoolValue(val->Name, (val->dValue != 0)); nuclear@0: else if (val->Type == JSON_String) nuclear@0: SetValue(val->Name, val->Value); nuclear@0: else if (val->Type == JSON_Array) nuclear@0: { nuclear@0: // Create a copy of the array nuclear@0: JSON* value = val->Copy(); nuclear@0: Values.PushBack(value); nuclear@0: ValMap.Set(value->Name, value); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: void Profile::SetValue(const char* key, const char* val) nuclear@0: { nuclear@0: if (key == NULL || val == NULL) nuclear@0: return; nuclear@0: nuclear@0: JSON* value = NULL; nuclear@0: if (ValMap.Get(key, &value)) nuclear@0: { nuclear@0: value->Value = val; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: value = JSON::CreateString(val); nuclear@0: value->Name = key; nuclear@0: nuclear@0: Values.PushBack(value); nuclear@0: ValMap.Set(key, value); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: void Profile::SetBoolValue(const char* key, bool val) nuclear@0: { nuclear@0: if (key == NULL) nuclear@0: return; nuclear@0: nuclear@0: JSON* value = NULL; nuclear@0: if (ValMap.Get(key, &value)) nuclear@0: { nuclear@0: value->dValue = val; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: value = JSON::CreateBool(val); nuclear@0: value->Name = key; nuclear@0: nuclear@0: Values.PushBack(value); nuclear@0: ValMap.Set(key, value); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: void Profile::SetIntValue(const char* key, int val) nuclear@0: { nuclear@0: SetDoubleValue(key, val); nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: void Profile::SetFloatValue(const char* key, float val) nuclear@0: { nuclear@0: SetDoubleValue(key, val); nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: void Profile::SetFloatValues(const char* key, const float* vals, int num_vals) nuclear@0: { nuclear@0: JSON* value = NULL; nuclear@0: int val_count = 0; nuclear@0: if (ValMap.Get(key, &value)) nuclear@0: { nuclear@0: if (value->Type == JSON_Array) nuclear@0: { nuclear@0: // truncate the existing array if fewer entries provided nuclear@0: int num_existing_vals = value->GetArraySize(); nuclear@0: for (int i=num_vals; iRemoveLast(); nuclear@0: nuclear@0: JSON* item = value->GetFirstItem(); nuclear@0: while (item && val_count < num_vals) nuclear@0: { nuclear@0: if (item->Type == JSON_Number) nuclear@0: item->dValue = vals[val_count]; nuclear@0: nuclear@0: item = value->GetNextItem(item); nuclear@0: val_count++; nuclear@0: } nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: return; // Maybe we should change the data type? nuclear@0: } nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: value = JSON::CreateArray(); nuclear@0: value->Name = key; nuclear@0: nuclear@0: Values.PushBack(value); nuclear@0: ValMap.Set(key, value); nuclear@0: } nuclear@0: nuclear@0: for (; val_count < num_vals; val_count++) nuclear@0: value->AddArrayNumber(vals[val_count]); nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: void Profile::SetDoubleValue(const char* key, double val) nuclear@0: { nuclear@0: JSON* value = NULL; nuclear@0: if (ValMap.Get(key, &value)) nuclear@0: { nuclear@0: value->dValue = val; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: value = JSON::CreateNumber(val); nuclear@0: value->Name = key; nuclear@0: nuclear@0: Values.PushBack(value); nuclear@0: ValMap.Set(key, value); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: void Profile::SetDoubleValues(const char* key, const double* vals, int num_vals) nuclear@0: { nuclear@0: JSON* value = NULL; nuclear@0: int val_count = 0; nuclear@0: if (ValMap.Get(key, &value)) nuclear@0: { nuclear@0: if (value->Type == JSON_Array) nuclear@0: { nuclear@0: // truncate the existing array if fewer entries provided nuclear@0: int num_existing_vals = value->GetArraySize(); nuclear@0: for (int i=num_vals; iRemoveLast(); nuclear@0: nuclear@0: JSON* item = value->GetFirstItem(); nuclear@0: while (item && val_count < num_vals) nuclear@0: { nuclear@0: if (item->Type == JSON_Number) nuclear@0: item->dValue = vals[val_count]; nuclear@0: nuclear@0: item = value->GetNextItem(item); nuclear@0: val_count++; nuclear@0: } nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: return; // Maybe we should change the data type? nuclear@0: } nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: value = JSON::CreateArray(); nuclear@0: value->Name = key; nuclear@0: nuclear@0: Values.PushBack(value); nuclear@0: ValMap.Set(key, value); nuclear@0: } nuclear@0: nuclear@0: for (; val_count < num_vals; val_count++) nuclear@0: value->AddArrayNumber(vals[val_count]); nuclear@0: } nuclear@0: nuclear@0: //------------------------------------------------------------------------------ nuclear@0: bool Profile::IsDefaultProfile() nuclear@0: { nuclear@0: return 0 == OVR::String::CompareNoCase("Default", GetValue(OVR_KEY_NAME)); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: } // namespace OVR