/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2022 * * * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * * software and associated documentation files (the "Software"), to deal in the Software * * without restriction, including without limitation the rights to use, copy, modify, * * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to the following * * conditions: * * * * The above copyright notice and this permission notice shall be included in all copies * * or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include namespace { constexpr const char* _loggerCat = "SoftwareIntegrationSession"; } // namespace namespace openspace { // Anonomous namespace namespace { const std::string INDENT{" "}; bool writeToFile(std::filesystem::path path, const std::vector& buffer, std::string& errorMessage) { try { std::basic_ofstream outFile{path, std::ios::out | std::ios::binary}; if(!outFile) { throw std::ofstream::failure{"Could not open file"}; } const auto bufferSize = static_cast(buffer.size()); outFile.write(reinterpret_cast(&bufferSize), sizeof(uint32_t)); outFile.write(buffer.data(), bufferSize); outFile.close(); return true; } catch (const std::ofstream::failure& e) { errorMessage = fmt::format("Could not write to the file \"{}\". {}.", path.string(), e.what()); return false; } } bool readFile(std::filesystem::path path, std::vector& buffer, std::string& errorMessage) { try { std::basic_ifstream inFile{path, std::ios::out | std::ios::binary}; if(!inFile) { throw std::ifstream::failure{"Could not open file"}; } uint32_t bufferSize; inFile.read(reinterpret_cast(&bufferSize), sizeof(uint32_t)); buffer.resize(bufferSize); inFile.read(buffer.data(), bufferSize); inFile.close(); return true; } catch (const std::ifstream::failure& e) { errorMessage = fmt::format("Couldn't read the file \"{}\". {}.", path.string(), e.what()); return false; } } bool saveSessionData(SyncableStorage& storage, const std::filesystem::path& filePath, std::string& errorMessage) { std::vector byteStream; storage.dump(byteStream); if (byteStream.size() == 0) { errorMessage = "Software Integration Storage is empty."; return false; } if (std::filesystem::exists(filePath)) { errorMessage = fmt::format("The file \"{}\" already exists.", filePath.filename().string()); return false; } if (!writeToFile(filePath, byteStream, errorMessage)) { return false; } return true; } } // namespace bool softwareintegration::Session::loadSessionData(SoftwareIntegrationModule* module, const std::string& filePathString, std::string& errorMessage ) { auto filePath = std::filesystem::path{filePathString}; if (!std::filesystem::exists(filePath) || !std::filesystem::is_regular_file(filePath)) { errorMessage = fmt::format("File {} doesn't exists...", filePathString); LERROR(errorMessage); return false; } std::vector byteStream; if (!readFile(filePath, byteStream, errorMessage)) { LERROR(errorMessage); return false; } try { module->_syncableStorage.store(byteStream); } catch (const std::exception& e) { errorMessage = fmt::format("Couldn't store loaded data in Software Integration storage", e.what()); LERROR(errorMessage); return false; } // Set large time steps for the GUI (so you for example // can see the movement of stars at 5000 years/second) // Values set in seconds: Real time, 5k years, // 10k year, 50k year, 100k year, 500k year, 1M year std::string largeTimeSteps = "{ 1.0, 157680000000.0, 315360000000.0," " 1576800000000.0, 3153600000000.0," " 15768000000000.0, 3153600000000.0 }"; global::scriptEngine->queueScript( fmt::format( "openspace.time.setDeltaTimeSteps({});", largeTimeSteps ), scripting::ScriptEngine::RemoteScripting::Yes ); return true; } bool softwareintegration::Session::saveSession(const std::string& wantedFileName, std::string& errorMessage) { auto softwareIntegrationModule = global::moduleEngine->module(); if (!softwareIntegrationModule) { errorMessage = "Software Integration Module not found."; return false; } auto dirPath = absPath("${USER_ASSETS}") / wantedFileName; if (std::filesystem::exists(dirPath)) { errorMessage = fmt::format("A saved session with the name \"{}\" already exists.", dirPath.filename().string()); return false; } if (!std::filesystem::create_directory(dirPath)) { errorMessage = fmt::format("Could not create the folder \"{}\" in the user assets folder.", dirPath); return false; } auto sessionDataFilePath = dirPath / std::filesystem::path{ dirPath.filename().string() + ".dat" }; if ( !saveSessionData( softwareIntegrationModule->_syncableStorage, sessionDataFilePath, errorMessage ) ) { return false; } auto filePath = dirPath / std::filesystem::path{ dirPath.filename().string() + ".asset" }; std::ofstream assetFile; try { assetFile.open(filePath); assetFile << "local nodes = {\n"; auto identifiers = softwareIntegrationModule->_syncableStorage.getAllIdentifiers(); bool isFirstSgn = true; for (auto& identifier : identifiers) { auto r = renderable(identifier); if (r == nullptr) continue; if (!isFirstSgn) { assetFile << ",\n"; } else { isFirstSgn = false; } auto properties = r->properties(); assetFile << INDENT << "{\n" << INDENT << INDENT << "GUI = {\n" << INDENT << INDENT << INDENT << "Name = " << r->property("Name")->getStringValue() << ",\n" << INDENT << INDENT << INDENT << "Path = \"/Software Integration\"" << "\n" << INDENT << INDENT << "},\n" << INDENT << INDENT << "Identifier = \"" << identifier << "\",\n" << INDENT << INDENT << "Renderable = {\n" << INDENT << INDENT << INDENT << "Identifier = \"" << identifier << "\",\n"; bool isFirstProp = true; for (auto p : properties) { if (!p) continue; // VOLATILE: This is because option (enum) properties does not play nice when parsed to string // Either name them all with a name that includes "Option" or add the name here if (p->identifier().find("Option") != std::string::npos) continue; if (!isFirstProp) { assetFile << ",\n"; } else { isFirstProp = false; } assetFile << INDENT << INDENT << INDENT << p->identifier() << " = "; std::string valueAsString = p->getStringValue(); if (std::string{ p->type().name() }.find("string") == std::string::npos) { for( auto pos = valueAsString.find('['); pos != std::string::npos; pos = valueAsString.find('[', ++pos) ) { valueAsString.replace(pos, 1, "{"); valueAsString.insert(++pos, 1, ' '); } for( auto pos = valueAsString.find(']'); pos != std::string::npos; pos = valueAsString.find(']', ++pos) ) { valueAsString.replace(pos, 1, "}"); valueAsString.insert(pos++, 1, ' '); } } assetFile << valueAsString; } assetFile << '\n' << INDENT << INDENT << "}\n" << INDENT << "}"; } assetFile << "\n}\n\n" << "local data = asset.localResource(\"" << sessionDataFilePath.filename().string() << "\")\n" << "asset.onInitialize(function ()\n" << INDENT << "openspace.softwareintegration.loadSessionData(data)\n" << INDENT << "for _, node in ipairs(nodes) do\n" << INDENT << INDENT << "openspace.addSceneGraphNode(node)\n" << INDENT << "end\n" << "end)\n\n" << "asset.onDeinitialize(function ()\n" << INDENT << "for i=1, #nodes do\n" << INDENT << INDENT << "openspace.removeSceneGraphNode(nodes[#nodes + 1 - i].Identifier)\n" << INDENT << "end\n" << "end)\n\n" << "asset.meta = {\n" << INDENT << fmt::format("Name = \"{} (Software Integration session)\",", wantedFileName) << "\n" << INDENT << "Version = \"1.0\",\n" << INDENT << "Description = [[]],\n" << INDENT << "Author = \"\",\n" << INDENT << "URL = \"\",\n" << INDENT << "License = \"\"\n" << "}\n"; assetFile.close(); return true; } catch (std::ifstream::failure& err) { errorMessage = fmt::format("An error occured when creating the asset file. {}.", err.what()); return false; } } } // namespace openspace