Profile* ProfileManager::CreateProfileObject(const char* user, ProfileType device, const char** device_name) { Lock::Locker lockScope(&ProfileLock); Profile* profile = NULL; switch (device) { case Profile_GenericHMD: *device_name = NULL; profile = new HMDProfile(Profile_GenericHMD, user); break; case Profile_RiftDK1: *device_name = "RiftDK1"; profile = new RiftDK1Profile(user); break; case Profile_RiftDKHD: *device_name = "RiftDKHD"; profile = new RiftDKHDProfile(user); break; case Profile_Unknown: break; } return profile; }
void MessageHandlerRef::SetHandler(MessageHandler* handler) { OVR_ASSERT(!handler || MessageHandlerImpl::FromHandler(handler)->pLock == pLock); Lock::Locker lockScope(pLock); SetHandler_NTS(handler); }
bool DeviceHandle::enumerateNext(const DeviceEnumerationArgs& args) { if (GetType() == Device_None) return false; Ptr<DeviceManagerImpl> managerKeepAlive; Lock::Locker lockScope(pImpl->GetLock()); DeviceCreateDesc* next = pImpl; // If manager was destroyed, we get removed from the list. if (!pImpl->pNext) return false; managerKeepAlive = next->GetManagerImpl(); OVR_ASSERT(managerKeepAlive); do { next = next->pNext; if (managerKeepAlive->Devices.IsNull(next)) { pImpl->Release(); pImpl = 0; return false; } } while(!args.MatchRule(next->Type, next->Enumerated)); next->AddRef(); pImpl->Release(); pImpl = next; return true; }
// Clear the local profile cache void ProfileManager::ClearProfileData() { Lock::Locker lockScope(&ProfileLock); ProfileCache.Clear(); Changed = false; }
// Removes an existing profile. Returns true if the profile was found and deleted // and returns false otherwise. bool ProfileManager::Delete(const Profile* profile) { Lock::Locker lockScope(&ProfileLock); if (OVR_strcmp(profile->Name, "default") == 0) return false; // don't delete a default profile if (CacheDevice == Profile_Unknown) LoadCache(profile->Type); // Look for the existence of this profile for (unsigned int i=0; i<ProfileCache.GetSize(); i++) { if (OVR_strcmp(profile->Name, ProfileCache[i]->Name) == 0) { if (OVR_strcmp(profile->Name, DefaultProfile) == 0) DefaultProfile.Clear(); ProfileCache.RemoveAt(i); Changed = true; return true; } } return false; }
// Returns the user id of a specific user in the list. The returned // memory is locally allocated and should not be stored or deleted. Returns NULL // if the index is invalid const char* ProfileManager::GetUser(unsigned int index) { Lock::Locker lockScope(&ProfileLock); if (ProfileCache == NULL) { // Load the cache LoadCache(false); if (ProfileCache == NULL) return NULL; } JSON* users = ProfileCache->GetItemByName("Users"); if (users && index < users->GetItemCount()) { JSON* user_item = users->GetItemByIndex(index); if (user_item) { JSON* user = user_item->GetFirstItem(); if (user) { JSON* userid = user_item->GetItemByName(OVR_KEY_USER); if (userid) return userid->Value.ToCStr(); } } } return NULL; }
// Clear the local profile cache void ProfileManager::ClearCache() { Lock::Locker lockScope(&ProfileLock); ProfileCache.Clear(); CacheDevice = Profile_Unknown; }
bool ProfileManager::CreateUser(const char* user, const char* name) { Lock::Locker lockScope(&ProfileLock); if (ProfileCache == NULL) { // Load the cache LoadCache(true); if (ProfileCache == NULL) return false; } JSON* users = ProfileCache->GetItemByName("Users"); if (users == NULL) { // Generate the User section users = JSON::CreateArray(); ProfileCache->AddItem("Users", users); //TODO: Insert this before the TaggedData } // Search for the pre-existence of this user JSON* user_item = users->GetFirstItem(); int index = 0; while (user_item) { JSON* userid = user_item->GetItemByName("User"); int compare = OVR_strcmp(user, userid->Value); if (compare == 0) { // The user already exists so simply update the fields JSON* name_item = user_item->GetItemByName("Name"); if (name_item && OVR_strcmp(name, name_item->Value) != 0) { name_item->Value = name; Changed = true; } return true; } else if (compare < 0) { // A new user should be placed before this item break; } user_item = users->GetNextItem(user_item); index++; } // Create and fill the user struct JSON* new_user = JSON::CreateObject(); new_user->AddStringItem(OVR_KEY_USER, user); new_user->AddStringItem(OVR_KEY_NAME, name); // user_item->AddStringItem("Password", password); if (user_item == NULL) users->AddArrayElement(new_user); else users->InsertArrayElement(index, new_user); Changed = true; return true; }
// Serializes the profiles to disk. void ProfileManager::SaveCache() { String path = GetProfilePath(true); Lock::Locker lockScope(&ProfileLock); // TODO: Since there is only a single device type now, a full tree overwrite // is sufficient but in the future a selective device branch replacement will // be necessary Ptr<JSON> root = *JSON::CreateObject(); root->AddNumberItem("Oculus Profile Version", PROFILE_VERSION); root->AddStringItem("CurrentProfile", DefaultProfile); root->AddNumberItem("ProfileCount", (double) ProfileCache.GetSize()); // Generate a JSON subtree for each profile for (unsigned int i=0; i<ProfileCache.GetSize(); i++) { Profile* profile = ProfileCache[i]; JSON* json_profile = JSON::CreateObject(); json_profile->Name = "Profile"; json_profile->AddStringItem("Name", profile->Name); const char* gender; switch (profile->GetGender()) { case Profile::Gender_Male: gender = "Male"; break; case Profile::Gender_Female: gender = "Female"; break; default: gender = "Unspecified"; } json_profile->AddStringItem("Gender", gender); json_profile->AddNumberItem("PlayerHeight", profile->PlayerHeight); json_profile->AddNumberItem("IPD", profile->IPD); if (profile->Type == Profile_RiftDK1) { RiftDK1Profile* riftdk1 = (RiftDK1Profile*)profile; JSON* json_riftdk1 = JSON::CreateObject(); json_profile->AddItem("RiftDK1", json_riftdk1); const char* eyecup = "A"; switch (riftdk1->EyeCups) { case RiftDK1Profile::EyeCup_A: eyecup = "A"; break; case RiftDK1Profile::EyeCup_B: eyecup = "B"; break; case RiftDK1Profile::EyeCup_C: eyecup = "C"; break; } json_riftdk1->AddStringItem("EyeCup", eyecup); json_riftdk1->AddNumberItem("LL", riftdk1->LL); json_riftdk1->AddNumberItem("LR", riftdk1->LR); json_riftdk1->AddNumberItem("RL", riftdk1->RL); json_riftdk1->AddNumberItem("RR", riftdk1->RR); } root->AddItem("Profile", json_profile); } root->Save(path); }
// Returns the number of stored profiles for this device type int ProfileManager::GetProfileCount(ProfileType device) { Lock::Locker lockScope(&ProfileLock); if (CacheDevice == Profile_Unknown) LoadCache(device); return (int)ProfileCache.GetSize(); }
// Resets the current orientation void SensorFusion::Reset() { Lock::Locker lockScope(Handler.GetHandlerLock()); Q = Quatf(); QUncorrected = Quatf(); Stage = 0; RunningTime = 0; MagNumReferences = 0; MagRefIdx = -1; GyroOffset = Vector3f(); }
// Serializes the profiles to disk. void ProfileManager::Save() { Lock::Locker lockScope(&ProfileLock); if (ProfileCache == NULL) return; // Save the profile to disk BasePath = GetBaseOVRPath(true); // create the base directory if it doesn't exist String path = GetProfilePath(); ProfileCache->Save(path); Changed = false; }
MessageHandlerRef::~MessageHandlerRef() { { Lock::Locker lockScope(pLock); if (pHandler) { pHandler = 0; RemoveNode(); } } MessageHandlerSharedLock.ReleaseLock(pLock); pLock = 0; }
bool ProfileManager::HasProfile(ProfileType device, const char* name) { Lock::Locker lockScope(&ProfileLock); if (CacheDevice == Profile_Unknown) LoadCache(device); for (unsigned i = 0; i< ProfileCache.GetSize(); i++) { if (ProfileCache[i] && OVR_strcmp(ProfileCache[i]->Name, name) == 0) return true; } return false; }
// Returns a specific profile object in the list. The returned memory should be // encapsulated in a Ptr<> object or released after use. Returns NULL if the index // is invalid Profile* ProfileManager::LoadProfile(ProfileType device, unsigned int index) { Lock::Locker lockScope(&ProfileLock); if (CacheDevice == Profile_Unknown) LoadCache(device); if (index < ProfileCache.GetSize()) { Profile* profile = ProfileCache[index]; return profile->Clone(); } else { return NULL; } }
// Returns the name of the profile that is marked as the current default user. const char* ProfileManager::GetDefaultProfileName(ProfileType device) { Lock::Locker lockScope(&ProfileLock); if (CacheDevice == Profile_Unknown) LoadCache(device); if (ProfileCache.GetSize() > 0) { OVR_strcpy(NameBuff, Profile::MaxNameLen, DefaultProfile); return NameBuff; } else { return NULL; } }
// Returns the number of stored profiles for this device type int ProfileManager::GetUserCount() { Lock::Locker lockScope(&ProfileLock); if (ProfileCache == NULL) { // Load the cache LoadCache(false); if (ProfileCache == NULL) return 0; } JSON* users = ProfileCache->GetItemByName("Users"); if (users == NULL) return 0; return users->GetItemCount(); }
// Marks a particular user as the current default user. bool ProfileManager::SetDefaultProfileName(ProfileType device, const char* name) { Lock::Locker lockScope(&ProfileLock); if (CacheDevice == Profile_Unknown) LoadCache(device); // TODO: I should verify that the user is valid if (ProfileCache.GetSize() > 0) { DefaultProfile = name; Changed = true; return true; } else { return false; } }
// Returns the profile name of a specific profile in the list. The returned // memory is locally allocated and should not be stored or deleted. Returns NULL // if the index is invalid const char* ProfileManager::GetProfileName(ProfileType device, unsigned int index) { Lock::Locker lockScope(&ProfileLock); if (CacheDevice == Profile_Unknown) LoadCache(device); if (index < ProfileCache.GetSize()) { Profile* profile = ProfileCache[index]; OVR_strcpy(NameBuff, Profile::MaxNameLen, profile->Name); return NameBuff; } else { return NULL; } }
bool ProfileManager::RemoveUser(const char* user) { Lock::Locker lockScope(&ProfileLock); if (ProfileCache == NULL) { // Load the cache LoadCache(false); if (ProfileCache == NULL) return true; } JSON* users = ProfileCache->GetItemByName("Users"); if (users == NULL) return true; // Remove this user from the User table JSON* user_item = users->GetFirstItem(); while (user_item) { JSON* userid = user_item->GetItemByName("User"); if (OVR_strcmp(user, userid->Value) == 0) { // Delete the user entry user_item->RemoveNode(); user_item->Release(); Changed = true; break; } user_item = users->GetNextItem(user_item); } // Now remove all data entries with this user tag JSON* tagged_data = ProfileCache->GetItemByName("TaggedData"); Array<JSON*> user_items; FilterTaggedData(tagged_data, "User", user, user_items); for (unsigned int i=0; i<user_items.GetSize(); i++) { user_items[i]->RemoveNode(); user_items[i]->Release(); Changed = true; } return Changed; }
// This is a simple predictive filter based only on extrapolating the smoothed, current angular velocity. // Note that both QP (the predicted future orientation) and Q (the current orientation) are both maintained. Quatf SensorFusion::GetPredictedOrientation() { Lock::Locker lockScope(Handler.GetHandlerLock()); Quatf qP = QUncorrected; if (EnablePrediction) { #if 1 Vector3f angVelF = FAngV.SavitzkyGolaySmooth8(); float angVelFL = angVelF.Length(); if (angVelFL > 0.001f) { Vector3f rotAxisP = angVelF / angVelFL; float halfRotAngleP = angVelFL * PredictionDT * 0.5f; float sinaHRAP = sin(halfRotAngleP); Quatf deltaQP(rotAxisP.x*sinaHRAP, rotAxisP.y*sinaHRAP, rotAxisP.z*sinaHRAP, cos(halfRotAngleP)); qP = QUncorrected * deltaQP; } #else Quatd qpd = Quatd(Q.x,Q.y,Q.z,Q.w); int predictionStages = (int)(PredictionDT / DeltaT); Vector3f aa = FAngV.SavitzkyGolayDerivative12(); Vector3d aad = Vector3d(aa.x,aa.y,aa.z); Vector3f angVelF = FAngV.SavitzkyGolaySmooth8(); Vector3d avkd = Vector3d(angVelF.x,angVelF.y,angVelF.z); for (int i = 0; i < predictionStages; i++) { double angVelLengthd = avkd.Length(); Vector3d rotAxisd = avkd / angVelLengthd; double halfRotAngled = angVelLengthd * DeltaT * 0.5; double sinHRAd = sin(halfRotAngled); Quatd deltaQd = Quatd(rotAxisd.x*sinHRAd, rotAxisd.y*sinHRAd, rotAxisd.z*sinHRAd, cos(halfRotAngled)); qpd = qpd * deltaQd; // Update vel avkd += aad; } qP = Quatf((float)qpd.x,(float)qpd.y,(float)qpd.z,(float)qpd.w); #endif } return qP; }
// Saves a new or existing profile. Returns true on success or false on an // invalid or failed save. bool ProfileManager::Save(const Profile* profile) { Lock::Locker lockScope(&ProfileLock); if (OVR_strcmp(profile->Name, "default") == 0) return false; // don't save a default profile // TODO: I should also verify that this profile type matches the current cache if (CacheDevice == Profile_Unknown) LoadCache(profile->Type); // Look for the pre-existence of this profile bool added = false; for (unsigned int i=0; i<ProfileCache.GetSize(); i++) { int compare = OVR_strcmp(profile->Name, ProfileCache[i]->Name); if (compare == 0) { // TODO: I should do a proper field comparison to avoid unnecessary // overwrites and file saves // Replace the previous instance with the new profile ProfileCache[i] = *profile->Clone(); added = true; Changed = true; break; } } if (!added) { ProfileCache.PushBack(*profile->Clone()); if (ProfileCache.GetSize() == 1) CacheDevice = profile->Type; Changed = true; } return true; }
// Returns a profile object for a particular device and user name. The returned // memory should be encapsulated in a Ptr<> object or released after use. Returns // NULL if the profile is not found Profile* ProfileManager::LoadProfile(ProfileType device, const char* user) { if (user == NULL) return NULL; Lock::Locker lockScope(&ProfileLock); if (CacheDevice == Profile_Unknown) LoadCache(device); for (unsigned int i=0; i<ProfileCache.GetSize(); i++) { if (OVR_strcmp(user, ProfileCache[i]->Name) == 0) { // Found the requested user profile Profile* profile = ProfileCache[i]; return profile->Clone(); } } return NULL; }
//----------------------------------------------------------------------------- Profile* ProfileManager::GetTaggedProfile(const char** tag_names, const char** tags, int num_tags) { Lock::Locker lockScope(&ProfileLock); if (ProfileCache == NULL) { // Load the cache LoadCache(false); if (ProfileCache == NULL) return NULL; } JSON* tagged_data = ProfileCache->GetItemByName("TaggedData"); OVR_ASSERT(tagged_data); if (tagged_data == NULL) return NULL; Profile* profile = new Profile(BasePath); JSON* vals = FindTaggedData(tagged_data, tag_names, tags, num_tags); if (vals) { JSON* item = vals->GetFirstItem(); while (item) { //printf("Add %s, %s\n", item->Name.ToCStr(), item->Value.ToCStr()); //profile->Settings.Set(item->Name, item->Value); profile->SetValue(item); item = vals->GetNextItem(item); } return profile; } else { profile->Release(); return NULL; } }
// A predictive filter based on extrapolating the smoothed, current angular velocity Quatf SensorFusion::GetPredictedOrientation(float pdt) { Lock::Locker lockScope(Handler.GetHandlerLock()); Quatf qP = Q; if (EnablePrediction) { // This method assumes a constant angular velocity Vector3f angVelF = FAngV.SavitzkyGolaySmooth8(); float angVelFL = angVelF.Length(); // Force back to raw measurement angVelF = AngV; angVelFL = AngV.Length(); // Dynamic prediction interval: Based on angular velocity to reduce vibration const float minPdt = 0.001f; const float slopePdt = 0.1f; float newpdt = pdt; float tpdt = minPdt + slopePdt * angVelFL; if (tpdt < pdt) newpdt = tpdt; //LogText("PredictonDTs: %d\n",(int)(newpdt / PredictionTimeIncrement + 0.5f)); if (angVelFL > 0.001f) { Vector3f rotAxisP = angVelF / angVelFL; float halfRotAngleP = angVelFL * newpdt * 0.5f; float sinaHRAP = sin(halfRotAngleP); Quatf deltaQP(rotAxisP.x*sinaHRAP, rotAxisP.y*sinaHRAP, rotAxisP.z*sinaHRAP, cos(halfRotAngleP)); qP = Q * deltaQP; } } return qP; }
//----------------------------------------------------------------------------- Profile* ProfileManager::GetProfile(const ProfileDeviceKey& deviceKey, const char* user) { Lock::Locker lockScope(&ProfileLock); if (ProfileCache == NULL) { // Load the cache LoadCache(false); if (ProfileCache == NULL) return NULL; } Profile* profile = new Profile(BasePath); if (deviceKey.Valid) { if (!profile->LoadDeviceProfile(deviceKey) && (user == NULL)) { profile->Release(); return NULL; } } if (user) { const char* product_str = deviceKey.ProductName.IsEmpty() ? NULL : deviceKey.ProductName.ToCStr(); const char* serial_str = deviceKey.PrintedSerial.IsEmpty() ? NULL : deviceKey.PrintedSerial.ToCStr(); if (!profile->LoadProfile(ProfileCache.GetPtr(), user, product_str, serial_str)) { profile->Release(); return NULL; } } return profile; }
//----------------------------------------------------------------------------- bool ProfileManager::SetTaggedProfile(const char** tag_names, const char** tags, int num_tags, Profile* profile) { Lock::Locker lockScope(&ProfileLock); if (ProfileCache == NULL) { // Load the cache LoadCache(true); if (ProfileCache == NULL) return false; // TODO: Generate a new profile DB } JSON* tagged_data = ProfileCache->GetItemByName("TaggedData"); OVR_ASSERT(tagged_data); if (tagged_data == NULL) return false; // Get the cached tagged data section JSON* vals = FindTaggedData(tagged_data, tag_names, tags, num_tags); if (vals == NULL) { JSON* tagged_item = JSON::CreateObject(); JSON* taglist = JSON::CreateArray(); for (int i=0; i<num_tags; i++) { JSON* k = JSON::CreateObject(); k->AddStringItem(tag_names[i], tags[i]); taglist->AddArrayElement(k); } vals = JSON::CreateObject(); tagged_item->AddItem("tags", taglist); tagged_item->AddItem("vals", vals); tagged_data->AddArrayElement(tagged_item); } // Now add or update each profile setting in cache for (unsigned int i=0; i<profile->Values.GetSize(); i++) { JSON* value = profile->Values[i]; bool found = false; JSON* item = vals->GetFirstItem(); while (item) { if (value->Name == item->Name) { // Don't allow a pre-existing type to be overridden OVR_ASSERT(value->Type == item->Type); if (value->Type == item->Type) { // Check for the same value if (value->Type == JSON_Array) { // Update each array item if (item->GetArraySize() == value->GetArraySize()) { // Update each value (assumed to be basic types and not array of objects) JSON* value_element = value->GetFirstItem(); JSON* item_element = item->GetFirstItem(); while (item_element && value_element) { if (value_element->Type == JSON_String) { if (item_element->Value != value_element->Value) { // Overwrite the changed value and mark for file update item_element->Value = value_element->Value; Changed = true; } } else { if (item_element->dValue != value_element->dValue) { // Overwrite the changed value and mark for file update item_element->dValue = value_element->dValue; Changed = true; } } value_element = value->GetNextItem(value_element); item_element = item->GetNextItem(item_element); } } else { // if the array size changed, simply create a new one // TODO: Create the new array } } else if (value->Type == JSON_String) { if (item->Value != value->Value) { // Overwrite the changed value and mark for file update item->Value = value->Value; Changed = true; } } else { if (item->dValue != value->dValue) { // Overwrite the changed value and mark for file update item->dValue = value->dValue; Changed = true; } } } else { return false; } found = true; break; } item = vals->GetNextItem(item); } if (!found) { // Add the new value Changed = true; if (value->Type == JSON_String) vals->AddStringItem(value->Name, value->Value); else if (value->Type == JSON_Bool) vals->AddBoolItem(value->Name, ((int)value->dValue != 0)); else if (value->Type == JSON_Number) vals->AddNumberItem(value->Name, value->dValue); else if (value->Type == JSON_Array) vals->AddItem(value->Name, value->Copy()); else { OVR_ASSERT(false); Changed = false; } } } return true; }
// Populates the local profile cache. This occurs on the first access of the profile // data. All profile operations are performed against the local cache until the // ProfileManager is released or goes out of scope at which time the cache is serialized // to disk. void ProfileManager::LoadCache(bool create) { Lock::Locker lockScope(&ProfileLock); ClearProfileData(); String path = GetProfilePath(); Ptr<JSON> root = *JSON::Load(path); if (root == NULL) { path = BasePath + "/Profiles.json"; // look for legacy profile root = *JSON::Load(path); if (root == NULL) { if (create) { // Generate a skeleton profile database root = *JSON::CreateObject(); root->AddNumberItem("Oculus Profile Version", 2.0); root->AddItem("Users", JSON::CreateArray()); root->AddItem("TaggedData", JSON::CreateArray()); ProfileCache = root; } return; } // Verify the legacy version JSON* version_item = root->GetFirstItem(); if (version_item->Name == "Oculus Profile Version") { int major = atoi(version_item->Value.ToCStr()); if (major != 1) return; // don't use the file on unsupported major version number } else { return; // invalid file } // Convert the legacy format to the new database format LoadV1Profiles(root); } else { // Verify the file format and version JSON* version_item = root->GetFirstItem(); if (version_item->Name == "Oculus Profile Version") { int major = atoi(version_item->Value.ToCStr()); if (major != 2) return; // don't use the file on unsupported major version number } else { return; // invalid file } ProfileCache = root; // store the database contents for traversal } }
// Poplulates the local profile cache. This occurs on the first access of the profile // data. All profile operations are performed against the local cache until the // ProfileManager is released or goes out of scope at which time the cache is serialized // to disk. void ProfileManager::LoadCache(ProfileType device) { Lock::Locker lockScope(&ProfileLock); ClearCache(); String path = GetProfilePath(false); Ptr<JSON> root = *JSON::Load(path); if (!root || root->GetItemCount() < 3) return; // First read the file type and version to make sure this is a valid file JSON* item0 = root->GetFirstItem(); JSON* item1 = root->GetNextItem(item0); JSON* item2 = root->GetNextItem(item1); if (item0->Name == "Oculus Profile Version") { int major = atoi(item0->Value.ToCStr()); if (major > MAX_PROFILE_MAJOR_VERSION) return; // don't parse the file on unsupported major version number } else { return; } DefaultProfile = item1->Value; // Read the number of profiles int profileCount = (int)item2->dValue; JSON* profileItem = item2; for (int p=0; p<profileCount; p++) { profileItem = root->GetNextItem(profileItem); if (profileItem == NULL) break; if (profileItem->Name == "Profile") { // Read the required Name field const char* profileName; JSON* item = profileItem->GetFirstItem(); if (item && (item->Name == "Name")) { profileName = item->Value; } else { return; // invalid field } const char* deviceName = 0; bool deviceFound = false; Ptr<Profile> profile = *CreateProfileObject(profileName, device, &deviceName); // Read the base profile fields. if (profile) { while (item = profileItem->GetNextItem(item), item) { if (item->Type != JSON_Object) { profile->ParseProperty(item->Name, item->Value); } else { // Search for the matching device to get device specific fields if (!deviceFound && deviceName && OVR_strcmp(item->Name, deviceName) == 0) { deviceFound = true; for (JSON* deviceItem = item->GetFirstItem(); deviceItem; deviceItem = item->GetNextItem(deviceItem)) { profile->ParseProperty(deviceItem->Name, deviceItem->Value); } } } } } // Add the new profile ProfileCache.PushBack(profile); } } CacheDevice = device; }
// Serializes the profiles to disk. void ProfileManager::SaveCache() { String path = GetProfilePath(true); Lock::Locker lockScope(&ProfileLock); Ptr<JSON> oldroot = *JSON::Load(path); if (oldroot) { if (oldroot->GetItemCount() >= 3) { JSON* item0 = oldroot->GetFirstItem(); JSON* item1 = oldroot->GetNextItem(item0); oldroot->GetNextItem(item1); if (item0->Name == "Oculus Profile Version") { int major = atoi(item0->Value.ToCStr()); if (major > MAX_PROFILE_MAJOR_VERSION) oldroot.Clear(); // don't use the file on unsupported major version number } else { oldroot.Clear(); } } else { oldroot.Clear(); } } // Create a new json root Ptr<JSON> root = *JSON::CreateObject(); root->AddNumberItem("Oculus Profile Version", PROFILE_VERSION); root->AddStringItem("CurrentProfile", DefaultProfile); root->AddNumberItem("ProfileCount", (double) ProfileCache.GetSize()); // Generate a JSON subtree for each profile for (unsigned int i=0; i<ProfileCache.GetSize(); i++) { Profile* profile = ProfileCache[i]; // Write the base profile information JSON* json_profile = JSON::CreateObject(); json_profile->Name = "Profile"; json_profile->AddStringItem("Name", profile->Name); const char* gender; switch (profile->GetGender()) { case Profile::Gender_Male: gender = "Male"; break; case Profile::Gender_Female: gender = "Female"; break; default: gender = "Unspecified"; } json_profile->AddStringItem("Gender", gender); json_profile->AddNumberItem("PlayerHeight", profile->PlayerHeight); json_profile->AddNumberItem("IPD", profile->IPD); const char* device_name = NULL; // Create a device-specific subtree for the cached device if (profile->Type == Profile_RiftDK1) { device_name = "RiftDK1"; RiftDK1Profile* rift = (RiftDK1Profile*)profile; JSON* json_rift = JSON::CreateObject(); json_profile->AddItem(device_name, json_rift); const char* eyecup = "A"; switch (rift->EyeCups) { case EyeCup_A: eyecup = "A"; break; case EyeCup_B: eyecup = "B"; break; case EyeCup_C: eyecup = "C"; break; } json_rift->AddStringItem("EyeCup", eyecup); json_rift->AddNumberItem("LL", rift->LL); json_rift->AddNumberItem("LR", rift->LR); json_rift->AddNumberItem("RL", rift->RL); json_rift->AddNumberItem("RR", rift->RR); } else if (profile->Type == Profile_RiftDKHD) { device_name = "RiftDKHD"; RiftDKHDProfile* rift = (RiftDKHDProfile*)profile; JSON* json_rift = JSON::CreateObject(); json_profile->AddItem(device_name, json_rift); const char* eyecup = "A"; switch (rift->EyeCups) { case EyeCup_A: eyecup = "A"; break; case EyeCup_B: eyecup = "B"; break; case EyeCup_C: eyecup = "C"; break; } json_rift->AddStringItem("EyeCup", eyecup); //json_rift->AddNumberItem("LL", rift->LL); //json_rift->AddNumberItem("LR", rift->LR); //json_rift->AddNumberItem("RL", rift->RL); //json_rift->AddNumberItem("RR", rift->RR); } // There may be multiple devices stored per user, but only a single // device is represented by this root. We don't want to overwrite // the other devices so we need to examine the older root // and merge previous devices into new json root if (oldroot) { JSON* old_profile = oldroot->GetFirstItem(); while (old_profile) { if (old_profile->Name == "Profile") { JSON* profile_name = old_profile->GetItemByName("Name"); if (profile_name && OVR_strcmp(profile->Name, profile_name->Value) == 0) { // Now that we found the user in the older root, add all the // object children to the new root - except for the one for the // current device JSON* old_item = old_profile->GetFirstItem(); while (old_item) { if (old_item->Type == JSON_Object && (device_name == NULL || OVR_strcmp(old_item->Name, device_name) != 0)) { JSON* old_device = old_item; old_item = old_profile->GetNextItem(old_item); // remove the node from the older root to avoid multiple reference old_device->RemoveNode(); // add the node pointer to the new root json_profile->AddItem(old_device->Name, old_device); } else { old_item = old_profile->GetNextItem(old_item); } } break; } } old_profile = oldroot->GetNextItem(old_profile); } } // Add the completed user profile to the new root root->AddItem("Profile", json_profile); } // Save the profile to disk root->Save(path); }