// 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); }
// Writes the current calibration for a particular device to a device profile file // sensor - the sensor that was calibrated // cal_name - an optional name for the calibration or default if cal_name == NULL bool SensorFusion::SaveMagCalibration(const char* calibrationName) const { if (CachedSensorInfo.SerialNumber[0] == 0 || !HasMagCalibration()) return false; // A named calibration may be specified for calibration in different // environments, otherwise the default calibration is used if (calibrationName == NULL) calibrationName = "default"; // Generate a mag calibration event JSON* calibration = JSON::CreateObject(); // (hardcoded for now) the measurement and representation method calibration->AddStringItem("Version", "2.0"); calibration->AddStringItem("Name", "default"); // time stamp the calibration char time_str[64]; #if defined(OVR_OS_WIN32) and !defined(__MINGW32__) struct tm caltime; localtime_s(&caltime, &MagCalibrationTime); strftime(time_str, 64, "%Y-%m-%d %H:%M:%S", &caltime); #else struct tm* caltime; caltime = localtime(&MagCalibrationTime); strftime(time_str, 64, "%Y-%m-%d %H:%M:%S", caltime); #endif calibration->AddStringItem("Time", time_str); // write the full calibration matrix char matrix[256]; Matrix4f calmat = GetMagCalibration(); calmat.ToString(matrix, 256); calibration->AddStringItem("CalibrationMatrix", matrix); // save just the offset, for backwards compatibility // this can be removed when we don't want to support 0.2.4 anymore Vector3f center(calmat.M[0][3], calmat.M[1][3], calmat.M[2][3]); Matrix4f tmp = calmat; tmp.M[0][3] = tmp.M[1][3] = tmp.M[2][3] = 0; tmp.M[3][3] = 1; center = tmp.Inverted().Transform(center); Matrix4f oldcalmat; oldcalmat.M[0][3] = center.x; oldcalmat.M[1][3] = center.y; oldcalmat.M[2][3] = center.z; oldcalmat.ToString(matrix, 256); calibration->AddStringItem("Calibration", matrix); String path = GetBaseOVRPath(true); path += "/Devices.json"; // Look for a prexisting device file to edit Ptr<JSON> root = *JSON::Load(path); if (root) { // Quick sanity check of the file type and format before we parse it JSON* version = root->GetFirstItem(); if (version && version->Name == "Oculus Device Profile Version") { int major = atoi(version->Value.ToCStr()); if (major > MAX_DEVICE_PROFILE_MAJOR_VERSION) { // don't use the file on unsupported major version number root->Release(); root = NULL; } } else { root->Release(); root = NULL; } } JSON* device = NULL; if (root) { device = root->GetFirstItem(); // skip the header device = root->GetNextItem(device); while (device) { // Search for a previous calibration with the same name for this device // and remove it before adding the new one if (device->Name == "Device") { JSON* item = device->GetItemByName("Serial"); if (item && item->Value == CachedSensorInfo.SerialNumber) { // found an entry for this device item = device->GetNextItem(item); while (item) { if (item->Name == "MagCalibration") { JSON* name = item->GetItemByName("Name"); if (name && name->Value == calibrationName) { // found a calibration of the same name item->RemoveNode(); item->Release(); break; } } item = device->GetNextItem(item); } // update the auto-mag flag item = device->GetItemByName("EnableYawCorrection"); if (item) item->dValue = (double)EnableYawCorrection; else device->AddBoolItem("EnableYawCorrection", EnableYawCorrection); break; } } device = root->GetNextItem(device); } } else { // Create a new device root root = *JSON::CreateObject(); root->AddStringItem("Oculus Device Profile Version", "1.0"); } if (device == NULL) { device = JSON::CreateObject(); device->AddStringItem("Product", CachedSensorInfo.ProductName); device->AddNumberItem("ProductID", CachedSensorInfo.ProductId); device->AddStringItem("Serial", CachedSensorInfo.SerialNumber); device->AddBoolItem("EnableYawCorrection", EnableYawCorrection); root->AddItem("Device", device); } // Create and the add the new calibration event to the device device->AddItem("MagCalibration", calibration); return root->Save(path); }
// 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); }
void ProfileManager::LoadV1Profiles(JSON* v1) { JSON* item0 = v1->GetFirstItem(); JSON* item1 = v1->GetNextItem(item0); JSON* item2 = v1->GetNextItem(item1); // Create the new profile database Ptr<JSON> root = *JSON::CreateObject(); root->AddNumberItem("Oculus Profile Version", 2.0); root->AddItem("Users", JSON::CreateArray()); root->AddItem("TaggedData", JSON::CreateArray()); ProfileCache = root; const char* default_dk1_user = 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 } // Read the user profile fields if (CreateUser(profileName, profileName)) { const char* tag_names[2] = {"User", "Product"}; const char* tags[2]; tags[0] = profileName; Ptr<Profile> user_profile = *CreateProfile(); user_profile->SetValue(OVR_KEY_NAME, profileName); float neckeye[2] = { 0, 0 }; item = profileItem->GetNextItem(item); while (item) { if (item->Type != JSON_Object) { if (item->Name == OVR_KEY_PLAYER_HEIGHT) { // Add an explicit eye height } if (item->Name == "NeckEyeHori") neckeye[0] = (float)item->dValue; else if (item->Name == "NeckEyeVert") neckeye[1] = (float)item->dValue; else user_profile->SetValue(item); } else { // Add the user/device tag values const char* device_name = item->Name.ToCStr(); Ptr<Profile> device_profile = *CreateProfile(); JSON* device_item = item->GetFirstItem(); while (device_item) { device_profile->SetValue(device_item); device_item = item->GetNextItem(device_item); } tags[1] = device_name; SetTaggedProfile(tag_names, tags, 2, device_profile); } item = profileItem->GetNextItem(item); } // Add an explicit eye-height field float player_height = user_profile->GetFloatValue(OVR_KEY_PLAYER_HEIGHT, OVR_DEFAULT_PLAYER_HEIGHT); if (player_height > 0) { char gender[16]; user_profile->GetValue(OVR_KEY_GENDER, gender, 16); const float EYE_TO_HEADTOP_RATIO = 0.44538f; const float MALE_AVG_HEAD_HEIGHT = 0.232f; const float FEMALE_AVG_HEAD_HEIGHT = 0.218f; // compute distance from top of skull to the eye float head_height; if (OVR_strcmp(gender, "Female") == 0) head_height = FEMALE_AVG_HEAD_HEIGHT; else head_height = MALE_AVG_HEAD_HEIGHT; float skull = EYE_TO_HEADTOP_RATIO * head_height; float eye_height = player_height - skull; user_profile->SetFloatValue(OVR_KEY_EYE_HEIGHT, eye_height); } // Convert NeckEye values to an array if (neckeye[0] > 0 && neckeye[1] > 0) user_profile->SetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, neckeye, 2); // Add the user tag values SetTaggedProfile(tag_names, tags, 1, user_profile); } } } // since V1 profiles were only for DK1, the assign the user to all DK1's const char* tag_names[1] = { "Product" }; const char* tags[1] = { "RiftDK1" }; Ptr<Profile> product_profile = *CreateProfile(); product_profile->SetValue("DefaultUser", default_dk1_user); SetTaggedProfile(tag_names, tags, 1, product_profile); }
// 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 } }