Files
OpenSpace/modules/softwareintegration/session/session.cpp
2022-06-22 18:52:47 -06:00

302 lines
12 KiB
C++

/*****************************************************************************************
* *
* 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 <modules/softwareintegration/session/session.h>
#include <modules/softwareintegration/softwareintegrationmodule.h>
#include <modules/softwareintegration/utils/syncablestorage.h>
#include <openspace/engine/globals.h>
#include <openspace/scripting/scriptengine.h>
#include <openspace/engine/moduleengine.h>
#include <openspace/rendering/renderable.h>
#include <openspace/query/query.h>
#include <ghoul/filesystem/filesystem.h>
#include <fstream>
#include <unordered_map>
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<std::byte>& buffer, std::string& errorMessage) {
try {
std::basic_ofstream<std::byte> outFile{path, std::ios::out | std::ios::binary};
if(!outFile) {
throw std::ofstream::failure{"Could not open file"};
}
const auto bufferSize = static_cast<uint32_t>(buffer.size());
outFile.write(reinterpret_cast<const std::byte*>(&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<std::byte>& buffer, std::string& errorMessage) {
try {
std::basic_ifstream<std::byte> 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<std::byte*>(&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<std::byte> 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<std::byte> 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<SoftwareIntegrationModule>();
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