ovr_sdk

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

initial 0.4.4
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 14 Jan 2015 06:51:16 +0200
parents
children
line source
1 /************************************************************************************
3 PublicHeader: None
4 Filename : OVR_Profile.cpp
5 Content : Structs and functions for loading and storing device profile settings
6 Created : February 14, 2013
7 Notes :
9 Profiles are used to store per-user settings that can be transferred and used
10 across multiple applications. For example, player IPD can be configured once
11 and reused for a unified experience across games. Configuration and saving of profiles
12 can be accomplished in game via the Profile API or by the official Oculus Configuration
13 Utility.
15 Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
17 Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
18 you may not use the Oculus VR Rift SDK except in compliance with the License,
19 which is provided at the time of installation or download, or which
20 otherwise accompanies this software in either electronic or hard copy form.
22 You may obtain a copy of the License at
24 http://www.oculusvr.com/licenses/LICENSE-3.2
26 Unless required by applicable law or agreed to in writing, the Oculus VR SDK
27 distributed under the License is distributed on an "AS IS" BASIS,
28 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29 See the License for the specific language governing permissions and
30 limitations under the License.
32 ************************************************************************************/
34 #include "OVR_Profile.h"
35 #include "OVR_JSON.h"
36 #include "Kernel/OVR_SysFile.h"
37 #include "Kernel/OVR_Allocator.h"
38 #include "OVR_Stereo.h"
40 #ifdef OVR_OS_WIN32
41 #define WIN32_LEAN_AND_MEAN
42 #include <Windows.h>
43 #include <Shlobj.h>
44 #elif defined(OVR_OS_MS) // Other Microsoft OSs
45 // Nothing, thanks.
46 #else
47 #include <dirent.h>
48 #include <sys/stat.h>
50 #ifdef OVR_OS_LINUX
51 #include <unistd.h>
52 #include <pwd.h>
53 #endif
55 #endif
57 #define PROFILE_VERSION 2.0
58 #define MAX_PROFILE_MAJOR_VERSION 2
59 #define MAX_DEVICE_PROFILE_MAJOR_VERSION 1
62 namespace OVR {
65 //-----------------------------------------------------------------------------
66 // ProfileDeviceKey
68 ProfileDeviceKey::ProfileDeviceKey(const HMDInfo* info) :
69 Valid(false)
70 {
71 if (info)
72 {
73 PrintedSerial = info->PrintedSerial;
74 ProductName = SanitizeProductName(info->ProductName);
75 ProductId = info->ProductId;
76 HmdType = info->HmdType;
78 if (ProductId != 0)
79 {
80 Valid = true;
81 }
82 }
83 else
84 {
85 ProductId = 0;
86 HmdType = HmdType_None;
87 }
88 }
90 String ProfileDeviceKey::SanitizeProductName(String productName)
91 {
92 String result;
94 if (!productName.IsEmpty())
95 {
96 const char* product_name = productName.ToCStr();
98 // First strip off "Oculus"
99 const char* oculus = strstr(product_name, "Oculus ");
100 if (oculus)
101 {
102 product_name = oculus + OVR_strlen("Oculus ");
103 }
105 // And remove spaces from the name
106 for (const char* s = product_name; *s != 0; s++)
107 {
108 if (*s != ' ')
109 {
110 result.AppendChar(*s);
111 }
112 }
113 }
115 return result;
116 }
120 //-----------------------------------------------------------------------------
121 // Returns the pathname of the JSON file containing the stored profiles
122 String GetBaseOVRPath(bool create_dir)
123 {
124 String path;
126 #if defined(OVR_OS_WIN32)
128 TCHAR data_path[MAX_PATH];
129 SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, NULL, 0, data_path);
130 path = String(data_path);
132 path += "/Oculus";
134 if (create_dir)
135 { // Create the Oculus directory if it doesn't exist
136 WCHAR wpath[128];
137 OVR::UTF8Util::DecodeString(wpath, path.ToCStr());
139 DWORD attrib = GetFileAttributes(wpath);
140 bool exists = attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY);
141 if (!exists)
142 {
143 CreateDirectory(wpath, NULL);
144 }
145 }
147 #elif defined(OVR_OS_OS) // Other Microsoft OSs
149 // TODO: figure this out.
150 OVR_UNUSED ( create_dir );
151 path = "";
153 #elif defined(OVR_OS_MAC)
155 const char* home = getenv("HOME");
156 path = home;
157 path += "/Library/Preferences/Oculus";
159 if (create_dir)
160 { // Create the Oculus directory if it doesn't exist
161 DIR* dir = opendir(path);
162 if (dir == NULL)
163 {
164 mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
165 }
166 else
167 {
168 closedir(dir);
169 }
170 }
172 #else
174 const char* home = getenv("HOME");
175 path = home;
176 path += "/.config/Oculus";
178 if (create_dir)
179 { // Create the Oculus directory if it doesn't exist
180 DIR* dir = opendir(path);
181 if (dir == NULL)
182 {
183 mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
184 }
185 else
186 {
187 closedir(dir);
188 }
189 }
191 #endif
193 return path;
194 }
196 String ProfileManager::GetProfilePath()
197 {
198 return BasePath + "/ProfileDB.json";
199 }
201 static JSON* FindTaggedData(JSON* data, const char** tag_names, const char** qtags, int num_qtags)
202 {
203 if (data == NULL || !(data->Name == "TaggedData") || data->Type != JSON_Array)
204 return NULL;
206 JSON* tagged_item = data->GetFirstItem();
207 while (tagged_item)
208 {
209 JSON* tags = tagged_item->GetItemByName("tags");
210 if (tags->Type == JSON_Array && num_qtags == tags->GetArraySize())
211 { // Check for a full tag match on each item
212 int num_matches = 0;
214 for (int k=0; k<num_qtags; k++)
215 {
216 JSON* tag = tags->GetFirstItem();
217 while (tag)
218 {
219 JSON* tagval = tag->GetFirstItem();
220 if (tagval && tagval->Name == tag_names[k])
221 {
222 if (tagval->Value == qtags[k])
223 num_matches++;
224 break;
225 }
226 tag = tags->GetNextItem(tag);
227 }
228 }
230 // if all tags were matched then copy the values into this Profile
231 if (num_matches == num_qtags)
232 {
233 JSON* vals = tagged_item->GetItemByName("vals");
234 return vals;
235 }
236 }
238 tagged_item = data->GetNextItem(tagged_item);
239 }
241 return NULL;
242 }
244 static void FilterTaggedData(JSON* data, const char* tag_name, const char* qtag, Array<JSON*>& items)
245 {
246 if (data == NULL || !(data->Name == "TaggedData") || data->Type != JSON_Array)
247 return;
249 JSON* tagged_item = data->GetFirstItem();
250 while (tagged_item)
251 {
252 JSON* tags = tagged_item->GetItemByName("tags");
253 if (tags->Type == JSON_Array)
254 { // Check for a tag match on the requested tag
256 JSON* tag = tags->GetFirstItem();
257 while (tag)
258 {
259 JSON* tagval = tag->GetFirstItem();
260 if (tagval && tagval->Name == tag_name)
261 {
262 if (tagval->Value == qtag)
263 { // Add this item to the output list
264 items.PushBack(tagged_item);
265 }
266 break;
267 }
268 tag = tags->GetNextItem(tag);
269 }
270 }
272 tagged_item = data->GetNextItem(tagged_item);
273 }
274 }
277 //-----------------------------------------------------------------------------
278 // ***** ProfileManager
280 template<> ProfileManager* OVR::SystemSingletonBase<ProfileManager>::SlowGetInstance()
281 {
282 static OVR::Lock lock;
283 OVR::Lock::Locker locker(&lock);
284 if (!SingletonInstance) SingletonInstance = new ProfileManager(true);
285 return SingletonInstance;
286 }
288 ProfileManager::ProfileManager(bool sys_register) :
289 Changed(false)
290 {
291 // Attempt to get the base path automatically, but this may fail
292 BasePath = GetBaseOVRPath(false);
294 if (sys_register)
295 PushDestroyCallbacks();
296 }
298 ProfileManager::~ProfileManager()
299 {
300 ClearProfileData();
301 }
303 void ProfileManager::OnSystemDestroy()
304 {
305 delete this;
306 }
308 // In the service process it is important to set the base path because this cannot be detected automatically
309 void ProfileManager::SetBasePath(String basePath)
310 {
311 if (basePath != BasePath)
312 {
313 BasePath = basePath;
314 LoadCache(false);
315 }
316 }
318 // Clear the local profile cache
319 void ProfileManager::ClearProfileData()
320 {
321 Lock::Locker lockScope(&ProfileLock);
323 ProfileCache.Clear();
324 Changed = false;
325 }
327 // Serializes the profiles to disk.
328 void ProfileManager::Save()
329 {
330 Lock::Locker lockScope(&ProfileLock);
332 if (ProfileCache == NULL)
333 return;
335 // Save the profile to disk
336 BasePath = GetBaseOVRPath(true); // create the base directory if it doesn't exist
337 String path = GetProfilePath();
338 ProfileCache->Save(path);
339 Changed = false;
340 }
342 // Returns a profile with all system default values
343 Profile* ProfileManager::GetDefaultProfile(HmdTypeEnum device)
344 {
345 // In the absence of any data, set some reasonable profile defaults.
346 // However, this is not future proof and developers should still
347 // provide reasonable default values for queried fields.
349 // Biometric data
350 Profile* profile = CreateProfile();
351 profile->SetValue(OVR_KEY_USER, "default");
352 profile->SetValue(OVR_KEY_NAME, "Default");
353 profile->SetValue(OVR_KEY_GENDER, OVR_DEFAULT_GENDER);
354 profile->SetFloatValue(OVR_KEY_PLAYER_HEIGHT, OVR_DEFAULT_PLAYER_HEIGHT);
355 profile->SetFloatValue(OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
356 profile->SetFloatValue(OVR_KEY_IPD, OVR_DEFAULT_IPD);
357 float half_ipd[2] = { OVR_DEFAULT_IPD / 2, OVR_DEFAULT_IPD / 2 };
358 profile->SetFloatValues(OVR_KEY_EYE_TO_NOSE_DISTANCE, half_ipd, 2);
359 float dist[2] = {OVR_DEFAULT_NECK_TO_EYE_HORIZONTAL, OVR_DEFAULT_NECK_TO_EYE_VERTICAL};
360 profile->SetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, dist, 2);
362 // Device specific data
363 if (device != HmdType_None)
364 {
365 if (device == HmdType_CrystalCoveProto || device == HmdType_DK2)
366 {
367 profile->SetValue("EyeCup", "A");
368 profile->SetIntValue(OVR_KEY_EYE_RELIEF_DIAL, OVR_DEFAULT_EYE_RELIEF_DIAL);
370 // TODO: These defaults are a little bogus and designed for continuity with 0.3
371 // eye-relief values. We need better measurement-based numbers in future releases
372 float max_eye_plate[2] = { 0.01965f + 0.018f, 0.01965f + 0.018f };
373 profile->SetFloatValues(OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE, max_eye_plate, 2);
374 }
375 else
376 { // DK1 and DKHD variants
377 profile->SetValue("EyeCup", "A");
378 profile->SetIntValue(OVR_KEY_EYE_RELIEF_DIAL, OVR_DEFAULT_EYE_RELIEF_DIAL);
380 // TODO: These defaults are a little bogus and designed for continuity with 0.3
381 // DK1 distortion. We need better measurement-based numbers in future releases
382 float max_eye_plate[2] = { 0.02357f + 0.017f, 0.02357f + 0.017f };
383 profile->SetFloatValues(OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE, max_eye_plate, 2);
384 }
385 }
387 return profile;
388 }
390 //------------------------------------------------------------------------------
391 void ProfileManager::Read()
392 {
393 LoadCache(false);
394 }
396 // Populates the local profile cache. This occurs on the first access of the profile
397 // data. All profile operations are performed against the local cache until the
398 // ProfileManager is released or goes out of scope at which time the cache is serialized
399 // to disk.
400 void ProfileManager::LoadCache(bool create)
401 {
402 Lock::Locker lockScope(&ProfileLock);
404 ClearProfileData();
406 String path = GetProfilePath();
408 Ptr<JSON> root = *JSON::Load(path);
409 if (root == NULL)
410 {
411 path = BasePath + "/Profiles.json"; // look for legacy profile
412 root = *JSON::Load(path);
414 if (root == NULL)
415 {
416 if (create)
417 { // Generate a skeleton profile database
418 root = *JSON::CreateObject();
419 root->AddNumberItem("Oculus Profile Version", 2.0);
420 root->AddItem("Users", JSON::CreateArray());
421 root->AddItem("TaggedData", JSON::CreateArray());
422 ProfileCache = root;
423 }
425 return;
426 }
428 // Verify the legacy version
429 JSON* version_item = root->GetFirstItem();
430 if (version_item->Name == "Oculus Profile Version")
431 {
432 int major = atoi(version_item->Value.ToCStr());
433 if (major != 1)
434 return; // don't use the file on unsupported major version number
435 }
436 else
437 {
438 return; // invalid file
439 }
441 // Convert the legacy format to the new database format
442 LoadV1Profiles(root);
443 }
444 else
445 {
446 // Verify the file format and version
447 JSON* version_item = root->GetFirstItem();
448 if (version_item && version_item->Name == "Oculus Profile Version")
449 {
450 int major = atoi(version_item->Value.ToCStr());
451 if (major != 2)
452 return; // don't use the file on unsupported major version number
453 }
454 else
455 {
456 return; // invalid file
457 }
459 ProfileCache = root; // store the database contents for traversal
460 }
461 }
463 void ProfileManager::LoadV1Profiles(JSON* v1)
464 {
465 JSON* item0 = v1->GetFirstItem();
466 JSON* item1 = v1->GetNextItem(item0);
467 JSON* item2 = v1->GetNextItem(item1);
469 OVR_ASSERT(item1 && item2);
470 if(!item1 || !item2)
471 return;
473 // Create the new profile database
474 Ptr<JSON> root = *JSON::CreateObject();
475 root->AddNumberItem("Oculus Profile Version", 2.0);
476 root->AddItem("Users", JSON::CreateArray());
477 root->AddItem("TaggedData", JSON::CreateArray());
478 ProfileCache = root;
480 const char* default_dk1_user = item1->Value;
482 // Read the number of profiles
483 int profileCount = (int)item2->dValue;
484 JSON* profileItem = item2;
486 for (int p=0; p<profileCount; p++)
487 {
488 profileItem = root->GetNextItem(profileItem);
489 if (profileItem == NULL)
490 break;
492 if (profileItem->Name == "Profile")
493 {
494 // Read the required Name field
495 const char* profileName;
496 JSON* item = profileItem->GetFirstItem();
498 if (item && (item->Name == "Name"))
499 {
500 profileName = item->Value;
501 }
502 else
503 {
504 return; // invalid field
505 }
507 // Read the user profile fields
508 if (CreateUser(profileName, profileName))
509 {
510 const char* tag_names[2] = {"User", "Product"};
511 const char* tags[2];
512 tags[0] = profileName;
514 Ptr<Profile> user_profile = *CreateProfile();
515 user_profile->SetValue(OVR_KEY_NAME, profileName);
517 float neckeye[2] = { 0, 0 };
519 item = profileItem->GetNextItem(item);
520 while (item)
521 {
522 if (item->Type != JSON_Object)
523 {
524 if (item->Name == OVR_KEY_PLAYER_HEIGHT)
525 { // Add an explicit eye height
527 }
528 if (item->Name == "NeckEyeHori")
529 neckeye[0] = (float)item->dValue;
530 else if (item->Name == "NeckEyeVert")
531 neckeye[1] = (float)item->dValue;
532 else
533 user_profile->SetValue(item);
534 }
535 else
536 {
537 // Add the user/device tag values
538 const char* device_name = item->Name.ToCStr();
539 Ptr<Profile> device_profile = *CreateProfile();
541 JSON* device_item = item->GetFirstItem();
542 while (device_item)
543 {
544 device_profile->SetValue(device_item);
545 device_item = item->GetNextItem(device_item);
546 }
548 tags[1] = device_name;
549 SetTaggedProfile(tag_names, tags, 2, device_profile);
550 }
552 item = profileItem->GetNextItem(item);
553 }
555 // Add an explicit eye-height field
556 float player_height = user_profile->GetFloatValue(OVR_KEY_PLAYER_HEIGHT,
557 OVR_DEFAULT_PLAYER_HEIGHT);
558 if (player_height > 0)
559 {
560 char gender[16];
561 user_profile->GetValue(OVR_KEY_GENDER, gender, 16);
563 const float EYE_TO_HEADTOP_RATIO = 0.44538f;
564 const float MALE_AVG_HEAD_HEIGHT = 0.232f;
565 const float FEMALE_AVG_HEAD_HEIGHT = 0.218f;
567 // compute distance from top of skull to the eye
568 float head_height;
569 if (OVR_strcmp(gender, "Female") == 0)
570 head_height = FEMALE_AVG_HEAD_HEIGHT;
571 else
572 head_height = MALE_AVG_HEAD_HEIGHT;
574 float skull = EYE_TO_HEADTOP_RATIO * head_height;
575 float eye_height = player_height - skull;
577 user_profile->SetFloatValue(OVR_KEY_EYE_HEIGHT, eye_height);
578 }
580 // Convert NeckEye values to an array
581 if (neckeye[0] > 0 && neckeye[1] > 0)
582 user_profile->SetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, neckeye, 2);
584 // Add the user tag values
585 SetTaggedProfile(tag_names, tags, 1, user_profile);
586 }
587 }
588 }
590 // since V1 profiles were only for DK1, the assign the user to all DK1's
591 const char* tag_names[1] = { "Product" };
592 const char* tags[1] = { "RiftDK1" };
593 Ptr<Profile> product_profile = *CreateProfile();
594 product_profile->SetValue("DefaultUser", default_dk1_user);
595 SetTaggedProfile(tag_names, tags, 1, product_profile);
596 }
598 // Returns the number of stored profiles for this device type
599 int ProfileManager::GetUserCount()
600 {
601 Lock::Locker lockScope(&ProfileLock);
603 if (ProfileCache == NULL)
604 { // Load the cache
605 LoadCache(false);
606 if (ProfileCache == NULL)
607 return 0;
608 }
610 JSON* users = ProfileCache->GetItemByName("Users");
611 if (users == NULL)
612 return 0;
614 return users->GetItemCount();
615 }
617 bool ProfileManager::CreateUser(const char* user, const char* name)
618 {
619 Lock::Locker lockScope(&ProfileLock);
621 if (ProfileCache == NULL)
622 { // Load the cache
623 LoadCache(true);
624 if (ProfileCache == NULL)
625 return false;
626 }
628 JSON* users = ProfileCache->GetItemByName("Users");
629 if (users == NULL)
630 { // Generate the User section
631 users = JSON::CreateArray();
632 ProfileCache->AddItem("Users", users);
633 //TODO: Insert this before the TaggedData
634 }
636 // Search for the pre-existence of this user
637 JSON* user_item = users->GetFirstItem();
638 int index = 0;
639 while (user_item)
640 {
641 JSON* userid = user_item->GetItemByName("User");
642 int compare = OVR_strcmp(user, userid->Value);
643 if (compare == 0)
644 { // The user already exists so simply update the fields
645 JSON* name_item = user_item->GetItemByName("Name");
646 if (name_item && OVR_strcmp(name, name_item->Value) != 0)
647 {
648 name_item->Value = name;
649 Changed = true;
650 }
651 return true;
652 }
653 else if (compare < 0)
654 { // A new user should be placed before this item
655 break;
656 }
658 user_item = users->GetNextItem(user_item);
659 index++;
660 }
662 // Create and fill the user struct
663 JSON* new_user = JSON::CreateObject();
664 new_user->AddStringItem(OVR_KEY_USER, user);
665 new_user->AddStringItem(OVR_KEY_NAME, name);
666 // user_item->AddStringItem("Password", password);
668 if (user_item == NULL)
669 users->AddArrayElement(new_user);
670 else
671 users->InsertArrayElement(index, new_user);
673 Changed = true;
674 return true;
675 }
677 bool ProfileManager::HasUser(const char* user)
678 {
679 Lock::Locker lockScope(&ProfileLock);
681 if (ProfileCache == NULL)
682 { // Load the cache
683 LoadCache(false);
684 if (ProfileCache == NULL)
685 return false;
686 }
688 JSON* users = ProfileCache->GetItemByName("Users");
689 if (users == NULL)
690 return false;
692 // Remove this user from the User table
693 JSON* user_item = users->GetFirstItem();
694 while (user_item)
695 {
696 JSON* userid = user_item->GetItemByName("User");
697 if (OVR_strcmp(user, userid->Value) == 0)
698 {
699 return true;
700 }
702 user_item = users->GetNextItem(user_item);
703 }
705 return false;
706 }
708 // Returns the user id of a specific user in the list. The returned
709 // memory is locally allocated and should not be stored or deleted. Returns NULL
710 // if the index is invalid
711 const char* ProfileManager::GetUser(unsigned int index)
712 {
713 Lock::Locker lockScope(&ProfileLock);
715 if (ProfileCache == NULL)
716 { // Load the cache
717 LoadCache(false);
718 if (ProfileCache == NULL)
719 return NULL;
720 }
722 JSON* users = ProfileCache->GetItemByName("Users");
724 if (users && index < users->GetItemCount())
725 {
726 JSON* user_item = users->GetItemByIndex(index);
727 if (user_item)
728 {
729 JSON* user = user_item->GetFirstItem();
730 if (user)
731 {
732 JSON* userid = user_item->GetItemByName(OVR_KEY_USER);
733 if (userid)
734 return userid->Value.ToCStr();
735 }
736 }
737 }
740 return NULL;
741 }
743 bool ProfileManager::RemoveUser(const char* user)
744 {
745 Lock::Locker lockScope(&ProfileLock);
747 if (ProfileCache == NULL)
748 { // Load the cache
749 LoadCache(false);
750 if (ProfileCache == NULL)
751 return true;
752 }
754 JSON* users = ProfileCache->GetItemByName("Users");
755 if (users == NULL)
756 return true;
758 // Remove this user from the User table
759 JSON* user_item = users->GetFirstItem();
760 while (user_item)
761 {
762 JSON* userid = user_item->GetItemByName("User");
763 if (OVR_strcmp(user, userid->Value) == 0)
764 { // Delete the user entry
765 user_item->RemoveNode();
766 user_item->Release();
767 Changed = true;
768 break;
769 }
771 user_item = users->GetNextItem(user_item);
772 }
774 // Now remove all data entries with this user tag
775 JSON* tagged_data = ProfileCache->GetItemByName("TaggedData");
776 Array<JSON*> user_items;
777 FilterTaggedData(tagged_data, "User", user, user_items);
778 for (unsigned int i=0; i<user_items.GetSize(); i++)
779 {
780 user_items[i]->RemoveNode();
781 user_items[i]->Release();
782 Changed = true;
783 }
785 return Changed;
786 }
788 Profile* ProfileManager::CreateProfile()
789 {
790 Profile* profile = new Profile(BasePath);
791 return profile;
792 }
794 const char* ProfileManager::GetDefaultUser(const ProfileDeviceKey& deviceKey)
795 {
796 const char* product_str = deviceKey.ProductName.IsEmpty() ? NULL : deviceKey.ProductName.ToCStr();
797 const char* serial_str = deviceKey.PrintedSerial.IsEmpty() ? NULL : deviceKey.PrintedSerial.ToCStr();
799 return GetDefaultUser(product_str, serial_str);
800 }
802 // Returns the name of the profile that is marked as the current default user.
803 const char* ProfileManager::GetDefaultUser(const char* product, const char* serial)
804 {
805 const char* tag_names[2] = {"Product", "Serial"};
806 const char* tags[2];
808 if (product && serial)
809 {
810 tags[0] = product;
811 tags[1] = serial;
812 // Look for a default user on this specific device
813 Ptr<Profile> p = *GetTaggedProfile(tag_names, tags, 2);
814 if (p == NULL)
815 { // Look for a default user on this product
816 p = *GetTaggedProfile(tag_names, tags, 1);
817 }
819 if (p)
820 {
821 const char* user = p->GetValue("DefaultUser");
822 if (user != NULL && user[0] != 0)
823 {
824 TempBuff = user;
825 return TempBuff.ToCStr();
826 }
827 }
828 }
830 return NULL;
831 }
833 //-----------------------------------------------------------------------------
834 bool ProfileManager::SetDefaultUser(const ProfileDeviceKey& deviceKey, const char* user)
835 {
836 const char* tag_names[2] = {"Product", "Serial"};
837 const char* tags[2];
839 const char* product_str = deviceKey.ProductName.IsEmpty() ? NULL : deviceKey.ProductName.ToCStr();
840 const char* serial_str = deviceKey.PrintedSerial.IsEmpty() ? NULL : deviceKey.PrintedSerial.ToCStr();
842 if (product_str && serial_str)
843 {
844 tags[0] = product_str;
845 tags[1] = serial_str;
847 Ptr<Profile> p = *CreateProfile();
848 p->SetValue("DefaultUser", user);
849 return SetTaggedProfile(tag_names, tags, 2, p);
850 }
852 return false;
853 }
855 //-----------------------------------------------------------------------------
856 Profile* ProfileManager::GetTaggedProfile(const char** tag_names, const char** tags, int num_tags)
857 {
858 Lock::Locker lockScope(&ProfileLock);
860 if (ProfileCache == NULL)
861 { // Load the cache
862 LoadCache(false);
863 if (ProfileCache == NULL)
864 return NULL;
865 }
867 JSON* tagged_data = ProfileCache->GetItemByName("TaggedData");
868 OVR_ASSERT(tagged_data);
869 if (tagged_data == NULL)
870 return NULL;
872 Profile* profile = new Profile(BasePath);
874 JSON* vals = FindTaggedData(tagged_data, tag_names, tags, num_tags);
875 if (vals)
876 {
877 JSON* item = vals->GetFirstItem();
878 while (item)
879 {
880 //printf("Add %s, %s\n", item->Name.ToCStr(), item->Value.ToCStr());
881 //profile->Settings.Set(item->Name, item->Value);
882 profile->SetValue(item);
883 item = vals->GetNextItem(item);
884 }
886 return profile;
887 }
888 else
889 {
890 profile->Release();
891 return NULL;
892 }
893 }
895 //-----------------------------------------------------------------------------
896 bool ProfileManager::SetTaggedProfile(const char** tag_names, const char** tags, int num_tags, Profile* profile)
897 {
898 Lock::Locker lockScope(&ProfileLock);
900 if (ProfileCache == NULL)
901 { // Load the cache
902 LoadCache(true);
903 if (ProfileCache == NULL)
904 return false; // TODO: Generate a new profile DB
905 }
907 JSON* tagged_data = ProfileCache->GetItemByName("TaggedData");
908 OVR_ASSERT(tagged_data);
909 if (tagged_data == NULL)
910 return false;
912 // Get the cached tagged data section
913 JSON* vals = FindTaggedData(tagged_data, tag_names, tags, num_tags);
914 if (vals == NULL)
915 {
916 JSON* tagged_item = JSON::CreateObject();
917 JSON* taglist = JSON::CreateArray();
918 for (int i=0; i<num_tags; i++)
919 {
920 JSON* k = JSON::CreateObject();
921 k->AddStringItem(tag_names[i], tags[i]);
922 taglist->AddArrayElement(k);
923 }
925 vals = JSON::CreateObject();
927 tagged_item->AddItem("tags", taglist);
928 tagged_item->AddItem("vals", vals);
929 tagged_data->AddArrayElement(tagged_item);
930 }
932 // Now add or update each profile setting in cache
933 for (unsigned int i=0; i<profile->Values.GetSize(); i++)
934 {
935 JSON* value = profile->Values[i];
937 bool found = false;
938 JSON* item = vals->GetFirstItem();
939 while (item)
940 {
941 if (value->Name == item->Name)
942 {
943 // Don't allow a pre-existing type to be overridden
944 OVR_ASSERT(value->Type == item->Type);
946 if (value->Type == item->Type)
947 { // Check for the same value
948 if (value->Type == JSON_Array)
949 { // Update each array item
950 if (item->GetArraySize() == value->GetArraySize())
951 { // Update each value (assumed to be basic types and not array of objects)
952 JSON* value_element = value->GetFirstItem();
953 JSON* item_element = item->GetFirstItem();
954 while (item_element && value_element)
955 {
956 if (value_element->Type == JSON_String)
957 {
958 if (item_element->Value != value_element->Value)
959 { // Overwrite the changed value and mark for file update
960 item_element->Value = value_element->Value;
961 Changed = true;
962 }
963 }
964 else {
965 if (item_element->dValue != value_element->dValue)
966 { // Overwrite the changed value and mark for file update
967 item_element->dValue = value_element->dValue;
968 Changed = true;
969 }
970 }
972 value_element = value->GetNextItem(value_element);
973 item_element = item->GetNextItem(item_element);
974 }
975 }
976 else
977 { // if the array size changed, simply create a new one
978 // TODO: Create the new array
979 }
980 }
981 else if (value->Type == JSON_String)
982 {
983 if (item->Value != value->Value)
984 { // Overwrite the changed value and mark for file update
985 item->Value = value->Value;
986 Changed = true;
987 }
988 }
989 else {
990 if (item->dValue != value->dValue)
991 { // Overwrite the changed value and mark for file update
992 item->dValue = value->dValue;
993 Changed = true;
994 }
995 }
996 }
997 else
998 {
999 return false;
1002 found = true;
1003 break;
1006 item = vals->GetNextItem(item);
1009 if (!found)
1010 { // Add the new value
1011 Changed = true;
1013 if (value->Type == JSON_String)
1014 vals->AddStringItem(value->Name, value->Value);
1015 else if (value->Type == JSON_Bool)
1016 vals->AddBoolItem(value->Name, ((int)value->dValue != 0));
1017 else if (value->Type == JSON_Number)
1018 vals->AddNumberItem(value->Name, value->dValue);
1019 else if (value->Type == JSON_Array)
1020 vals->AddItem(value->Name, value->Copy());
1021 else
1023 OVR_ASSERT(false);
1024 Changed = false;
1029 return true;
1032 //-----------------------------------------------------------------------------
1033 Profile* ProfileManager::GetDefaultUserProfile(const ProfileDeviceKey& deviceKey)
1035 const char* userName = GetDefaultUser(deviceKey);
1037 Profile* profile = GetProfile(deviceKey, userName);
1039 if (!profile)
1041 profile = GetDefaultProfile(deviceKey.HmdType);
1044 return profile;
1047 //-----------------------------------------------------------------------------
1048 Profile* ProfileManager::GetProfile(const ProfileDeviceKey& deviceKey, const char* user)
1050 Lock::Locker lockScope(&ProfileLock);
1052 if (ProfileCache == NULL)
1053 { // Load the cache
1054 LoadCache(false);
1055 if (ProfileCache == NULL)
1056 return NULL;
1059 Profile* profile = new Profile(BasePath);
1061 if (deviceKey.Valid)
1063 if (!profile->LoadDeviceProfile(deviceKey) && (user == NULL))
1065 profile->Release();
1066 return NULL;
1070 if (user)
1072 const char* product_str = deviceKey.ProductName.IsEmpty() ? NULL : deviceKey.ProductName.ToCStr();
1073 const char* serial_str = deviceKey.PrintedSerial.IsEmpty() ? NULL : deviceKey.PrintedSerial.ToCStr();
1075 if (!profile->LoadProfile(ProfileCache.GetPtr(), user, product_str, serial_str))
1077 profile->Release();
1078 return NULL;
1082 return profile;
1086 //-----------------------------------------------------------------------------
1087 // ***** Profile
1089 Profile::~Profile()
1091 ValMap.Clear();
1092 for (unsigned int i=0; i<Values.GetSize(); i++)
1093 Values[i]->Release();
1095 Values.Clear();
1098 bool Profile::Close()
1100 // TODO:
1101 return true;
1104 //-----------------------------------------------------------------------------
1105 void Profile::CopyItems(JSON* root, String prefix)
1107 JSON* item = root->GetFirstItem();
1108 while (item)
1110 String item_name;
1111 if (prefix.IsEmpty())
1112 item_name = item->Name;
1113 else
1114 item_name = prefix + "." + item->Name;
1116 if (item->Type == JSON_Object)
1117 { // recursively copy the children
1119 CopyItems(item, item_name);
1121 else
1123 //Settings.Set(item_name, item->Value);
1124 SetValue(item);
1127 item = root->GetNextItem(item);
1131 //-----------------------------------------------------------------------------
1132 bool Profile::LoadDeviceFile(unsigned int productId, const char* printedSerialNumber)
1134 if (printedSerialNumber[0] == 0)
1135 return false;
1137 String path = BasePath + "/Devices.json";
1139 // Load the device profiles
1140 Ptr<JSON> root = *JSON::Load(path);
1141 if (root == NULL)
1142 return false;
1144 // Quick sanity check of the file type and format before we parse it
1145 JSON* version = root->GetFirstItem();
1146 if (version && version->Name == "Oculus Device Profile Version")
1148 int major = atoi(version->Value.ToCStr());
1149 if (major > MAX_DEVICE_PROFILE_MAJOR_VERSION)
1150 return false; // don't parse the file on unsupported major version number
1152 else
1154 return false;
1157 JSON* device = root->GetNextItem(version);
1158 while (device)
1160 if (device->Name == "Device")
1162 JSON* product_item = device->GetItemByName("ProductID");
1163 JSON* serial_item = device->GetItemByName("Serial");
1164 if (product_item && serial_item &&
1165 (product_item->dValue == productId) && (serial_item->Value == printedSerialNumber))
1167 // found the entry for this device so recursively copy all the settings to the profile
1168 CopyItems(device, "");
1169 return true;
1173 device = root->GetNextItem(device);
1176 return false;
1179 #if 0
1180 //-----------------------------------------------------------------------------
1181 static int BCDByte(unsigned int byte)
1183 int digit1 = (byte >> 4) & 0x000f;
1184 int digit2 = byte & 0x000f;
1185 int decimal = digit1 * 10 + digit2;
1186 return decimal;
1188 #endif
1190 //-----------------------------------------------------------------------------
1191 bool Profile::LoadDeviceProfile(const ProfileDeviceKey& deviceKey)
1193 bool success = false;
1194 if (!deviceKey.Valid)
1195 return false;
1197 #if 0
1198 int dev_major = BCDByte((sinfo.Version >> 8) & 0x00ff);
1199 OVR_UNUSED(dev_major);
1200 //int dev_minor = BCDByte(sinfo.Version & 0xff);
1202 //if (dev_minor > 18)
1203 //{ // If the firmware supports hardware stored profiles then grab the device profile
1204 // from the sensor
1205 // TBD: Implement this
1206 //}
1207 //else
1209 #endif
1210 // Grab the model and serial number from the device and use it to access the device
1211 // profile file stored on the local machine
1212 success = LoadDeviceFile(deviceKey.ProductId, deviceKey.PrintedSerial);
1213 //}
1215 return success;
1218 //-----------------------------------------------------------------------------
1219 bool Profile::LoadUser(JSON* root,
1220 const char* user,
1221 const char* model_name,
1222 const char* device_serial)
1224 if (user == NULL)
1225 return false;
1227 // For legacy files, convert to old style names
1228 //if (model_name && OVR_strcmp(model_name, "Oculus Rift DK1") == 0)
1229 // model_name = "RiftDK1";
1231 bool user_found = false;
1232 JSON* data = root->GetItemByName("TaggedData");
1233 if (data)
1235 const char* tag_names[3];
1236 const char* tags[3];
1237 tag_names[0] = "User";
1238 tags[0] = user;
1239 int num_tags = 1;
1241 if (model_name)
1243 tag_names[num_tags] = "Product";
1244 tags[num_tags] = model_name;
1245 num_tags++;
1248 if (device_serial)
1250 tag_names[num_tags] = "Serial";
1251 tags[num_tags] = device_serial;
1252 num_tags++;
1255 // Retrieve all tag permutations
1256 for (int combos=1; combos<=num_tags; combos++)
1258 for (int i=0; i<(num_tags - combos + 1); i++)
1260 JSON* vals = FindTaggedData(data, tag_names+i, tags+i, combos);
1261 if (vals)
1263 if (i==0) // This tag-combination contains a user match
1264 user_found = true;
1266 // Add the values to the Profile. More specialized multi-tag values
1267 // will take precedence over and overwrite generalized ones
1268 // For example: ("Me","RiftDK1").IPD would overwrite ("Me").IPD
1269 JSON* item = vals->GetFirstItem();
1270 while (item)
1272 //printf("Add %s, %s\n", item->Name.ToCStr(), item->Value.ToCStr());
1273 //Settings.Set(item->Name, item->Value);
1274 SetValue(item);
1275 item = vals->GetNextItem(item);
1282 if (user_found)
1283 SetValue(OVR_KEY_USER, user);
1285 return user_found;
1289 //-----------------------------------------------------------------------------
1290 bool Profile::LoadProfile(JSON* root,
1291 const char* user,
1292 const char* device_model,
1293 const char* device_serial)
1295 if (!LoadUser(root, user, device_model, device_serial))
1296 return false;
1298 return true;
1302 //-----------------------------------------------------------------------------
1303 char* Profile::GetValue(const char* key, char* val, int val_length) const
1305 JSON* value = NULL;
1306 if (ValMap.Get(key, &value))
1308 OVR_strcpy(val, val_length, value->Value.ToCStr());
1309 return val;
1311 else
1313 val[0] = 0;
1314 return NULL;
1318 //-----------------------------------------------------------------------------
1319 const char* Profile::GetValue(const char* key)
1321 // Non-reentrant query. The returned buffer can only be used until the next call
1322 // to GetValue()
1323 JSON* value = NULL;
1324 if (ValMap.Get(key, &value))
1326 TempVal = value->Value;
1327 return TempVal.ToCStr();
1329 else
1331 return NULL;
1335 //-----------------------------------------------------------------------------
1336 int Profile::GetNumValues(const char* key) const
1338 JSON* value = NULL;
1339 if (ValMap.Get(key, &value))
1341 if (value->Type == JSON_Array)
1342 return value->GetArraySize();
1343 else
1344 return 1;
1346 else
1347 return 0;
1350 //-----------------------------------------------------------------------------
1351 bool Profile::GetBoolValue(const char* key, bool default_val) const
1353 JSON* value = NULL;
1354 if (ValMap.Get(key, &value) && value->Type == JSON_Bool)
1355 return (value->dValue != 0);
1356 else
1357 return default_val;
1360 //-----------------------------------------------------------------------------
1361 int Profile::GetIntValue(const char* key, int default_val) const
1363 JSON* value = NULL;
1364 if (ValMap.Get(key, &value) && value->Type == JSON_Number)
1365 return (int)(value->dValue);
1366 else
1367 return default_val;
1370 //-----------------------------------------------------------------------------
1371 float Profile::GetFloatValue(const char* key, float default_val) const
1373 JSON* value = NULL;
1374 if (ValMap.Get(key, &value) && value->Type == JSON_Number)
1375 return (float)(value->dValue);
1376 else
1377 return default_val;
1380 //-----------------------------------------------------------------------------
1381 int Profile::GetFloatValues(const char* key, float* values, int num_vals) const
1383 JSON* value = NULL;
1384 if (ValMap.Get(key, &value) && value->Type == JSON_Array)
1386 int val_count = Alg::Min(value->GetArraySize(), num_vals);
1387 JSON* item = value->GetFirstItem();
1388 int count=0;
1389 while (item && count < val_count)
1391 if (item->Type == JSON_Number)
1392 values[count] = (float)item->dValue;
1393 else
1394 break;
1396 count++;
1397 item = value->GetNextItem(item);
1400 return count;
1402 else
1404 return 0;
1408 //-----------------------------------------------------------------------------
1409 double Profile::GetDoubleValue(const char* key, double default_val) const
1411 JSON* value = NULL;
1412 if (ValMap.Get(key, &value) && value->Type == JSON_Number)
1413 return value->dValue;
1414 else
1415 return default_val;
1418 //-----------------------------------------------------------------------------
1419 int Profile::GetDoubleValues(const char* key, double* values, int num_vals) const
1421 JSON* value = NULL;
1422 if (ValMap.Get(key, &value) && value->Type == JSON_Array)
1424 int val_count = Alg::Min(value->GetArraySize(), num_vals);
1425 JSON* item = value->GetFirstItem();
1426 int count=0;
1427 while (item && count < val_count)
1429 if (item->Type == JSON_Number)
1430 values[count] = item->dValue;
1431 else
1432 break;
1434 count++;
1435 item = value->GetNextItem(item);
1438 return count;
1440 return 0;
1443 //-----------------------------------------------------------------------------
1444 void Profile::SetValue(JSON* val)
1446 if (val == NULL)
1447 return;
1449 if (val->Type == JSON_Number)
1450 SetDoubleValue(val->Name, val->dValue);
1451 else if (val->Type == JSON_Bool)
1452 SetBoolValue(val->Name, (val->dValue != 0));
1453 else if (val->Type == JSON_String)
1454 SetValue(val->Name, val->Value);
1455 else if (val->Type == JSON_Array)
1457 // Create a copy of the array
1458 JSON* value = val->Copy();
1459 Values.PushBack(value);
1460 ValMap.Set(value->Name, value);
1464 //-----------------------------------------------------------------------------
1465 void Profile::SetValue(const char* key, const char* val)
1467 if (key == NULL || val == NULL)
1468 return;
1470 JSON* value = NULL;
1471 if (ValMap.Get(key, &value))
1473 value->Value = val;
1475 else
1477 value = JSON::CreateString(val);
1478 value->Name = key;
1480 Values.PushBack(value);
1481 ValMap.Set(key, value);
1485 //-----------------------------------------------------------------------------
1486 void Profile::SetBoolValue(const char* key, bool val)
1488 if (key == NULL)
1489 return;
1491 JSON* value = NULL;
1492 if (ValMap.Get(key, &value))
1494 value->dValue = val;
1496 else
1498 value = JSON::CreateBool(val);
1499 value->Name = key;
1501 Values.PushBack(value);
1502 ValMap.Set(key, value);
1506 //-----------------------------------------------------------------------------
1507 void Profile::SetIntValue(const char* key, int val)
1509 SetDoubleValue(key, val);
1512 //-----------------------------------------------------------------------------
1513 void Profile::SetFloatValue(const char* key, float val)
1515 SetDoubleValue(key, val);
1518 //-----------------------------------------------------------------------------
1519 void Profile::SetFloatValues(const char* key, const float* vals, int num_vals)
1521 JSON* value = NULL;
1522 int val_count = 0;
1523 if (ValMap.Get(key, &value))
1525 if (value->Type == JSON_Array)
1527 // truncate the existing array if fewer entries provided
1528 int num_existing_vals = value->GetArraySize();
1529 for (int i=num_vals; i<num_existing_vals; i++)
1530 value->RemoveLast();
1532 JSON* item = value->GetFirstItem();
1533 while (item && val_count < num_vals)
1535 if (item->Type == JSON_Number)
1536 item->dValue = vals[val_count];
1538 item = value->GetNextItem(item);
1539 val_count++;
1542 else
1544 return; // Maybe we should change the data type?
1547 else
1549 value = JSON::CreateArray();
1550 value->Name = key;
1552 Values.PushBack(value);
1553 ValMap.Set(key, value);
1556 for (; val_count < num_vals; val_count++)
1557 value->AddArrayNumber(vals[val_count]);
1560 //-----------------------------------------------------------------------------
1561 void Profile::SetDoubleValue(const char* key, double val)
1563 JSON* value = NULL;
1564 if (ValMap.Get(key, &value))
1566 value->dValue = val;
1568 else
1570 value = JSON::CreateNumber(val);
1571 value->Name = key;
1573 Values.PushBack(value);
1574 ValMap.Set(key, value);
1578 //-----------------------------------------------------------------------------
1579 void Profile::SetDoubleValues(const char* key, const double* vals, int num_vals)
1581 JSON* value = NULL;
1582 int val_count = 0;
1583 if (ValMap.Get(key, &value))
1585 if (value->Type == JSON_Array)
1587 // truncate the existing array if fewer entries provided
1588 int num_existing_vals = value->GetArraySize();
1589 for (int i=num_vals; i<num_existing_vals; i++)
1590 value->RemoveLast();
1592 JSON* item = value->GetFirstItem();
1593 while (item && val_count < num_vals)
1595 if (item->Type == JSON_Number)
1596 item->dValue = vals[val_count];
1598 item = value->GetNextItem(item);
1599 val_count++;
1602 else
1604 return; // Maybe we should change the data type?
1607 else
1609 value = JSON::CreateArray();
1610 value->Name = key;
1612 Values.PushBack(value);
1613 ValMap.Set(key, value);
1616 for (; val_count < num_vals; val_count++)
1617 value->AddArrayNumber(vals[val_count]);
1620 //------------------------------------------------------------------------------
1621 bool Profile::IsDefaultProfile()
1623 return 0 == OVR::String::CompareNoCase("Default", GetValue(OVR_KEY_NAME));
1627 } // namespace OVR