diff --git a/include/openspace/interaction/tasks/convertrecfileversiontask.h b/include/openspace/interaction/tasks/convertrecfileversiontask.h new file mode 100644 index 0000000000..66125ee5bc --- /dev/null +++ b/include/openspace/interaction/tasks/convertrecfileversiontask.h @@ -0,0 +1,57 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2020 * + * * + * 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. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_CORE___CONVERTRECFILEVERSIONTASK___H__ +#define __OPENSPACE_CORE___CONVERTRECFILEVERSIONTASK___H__ + +#include +#include + +#include + +#include + + + +namespace openspace::interaction { + +class ConvertRecFileVersionTask : public Task { +public: + ConvertRecFileVersionTask(const ghoul::Dictionary& dictionary); + ~ConvertRecFileVersionTask(); + std::string description() override; + void perform(const Task::ProgressCallback& progressCallback) override; + static documentation::Documentation documentation(); + void convert(); + SessionRecording* sessRec; + +private: + std::string _inFilename; + std::string _inFilePath; + std::string _valueFunctionLua; +}; + +} // namespace openspace::interaction + +#endif //__OPENSPACE_CORE___CONVERTRECFILEVERSIONTASK___H__ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bf8dce02a9..71d71e97be 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -63,6 +63,7 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/interaction/shortcutmanager_lua.inl ${OPENSPACE_BASE_DIR}/src/interaction/websocketinputstate.cpp ${OPENSPACE_BASE_DIR}/src/interaction/websocketcamerastates.cpp + ${OPENSPACE_BASE_DIR}/src/interaction/tasks/convertrecfileversiontask.cpp ${OPENSPACE_BASE_DIR}/src/interaction/tasks/convertrecformattask.cpp ${OPENSPACE_BASE_DIR}/src/mission/mission.cpp ${OPENSPACE_BASE_DIR}/src/mission/missionmanager.cpp @@ -246,6 +247,7 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/interaction/shortcutmanager.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/websocketinputstate.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/websocketcamerastates.h + ${OPENSPACE_BASE_DIR}/include/openspace/interaction/tasks/convertrecfileversiontask.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/tasks/convertrecformattask.h ${OPENSPACE_BASE_DIR}/include/openspace/mission/mission.h ${OPENSPACE_BASE_DIR}/include/openspace/mission/missionmanager.h diff --git a/src/engine/globals.cpp b/src/engine/globals.cpp index 8a28ea5070..ef15f99aa6 100644 --- a/src/engine/globals.cpp +++ b/src/engine/globals.cpp @@ -184,7 +184,7 @@ interaction::NavigationHandler& gNavigationHandler() { } interaction::SessionRecording& gSessionRecording() { - static interaction::SessionRecording g; + static interaction::SessionRecording g(true); return g; } diff --git a/src/interaction/sessionrecording.cpp b/src/interaction/sessionrecording.cpp index 503d0f89fa..f357e1e77f 100644 --- a/src/interaction/sessionrecording.cpp +++ b/src/interaction/sessionrecording.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -60,7 +61,6 @@ namespace { constexpr const bool UsingTimeKeyframes = false; - } // namespace #include "sessionrecording_lua.inl" @@ -74,17 +74,24 @@ ConversionError::ConversionError(std::string msg) SessionRecording::SessionRecording() : properties::PropertyOwner({ "SessionRecording", "Session Recording" }) , _renderPlaybackInformation(RenderPlaybackInfo, false) +{} + +SessionRecording::SessionRecording(bool isGlobal) + : properties::PropertyOwner({ "SessionRecording", "Session Recording" }) + , _renderPlaybackInformation(RenderPlaybackInfo, false) { - auto fTask = FactoryManager::ref().factory(); - ghoul_assert(fTask, "No task factory existed"); - fTask->registerClass("ConvertRecFormatTask"); - fTask->registerClass("ConvertRecFileVersionTask"); - addProperty(_renderPlaybackInformation); + if (isGlobal) { + auto fTask = FactoryManager::ref().factory(); + ghoul_assert(fTask, "No task factory existed"); + fTask->registerClass("ConvertRecFormatTask"); + fTask->registerClass("ConvertRecFileVersionTask"); + addProperty(_renderPlaybackInformation); + } } SessionRecording::~SessionRecording() { // NOLINT - if (legacyVersion != nullptr) { - delete legacyVersion; + if (_legacyVersion != nullptr) { + delete _legacyVersion; } } @@ -1687,7 +1694,6 @@ void SessionRecording::readPlaybackFileHeader(const std::string filename, throw ConversionError("Cannot find the specified playback file to convert."); } - // Open in ASCII first conversionInFile.open(conversionInFilename, std::ifstream::in); // Read header @@ -1710,9 +1716,11 @@ void SessionRecording::readPlaybackFileHeader(const std::string filename, else { throw ConversionError("Unknown data type in header (needs Ascii or Binary)"); } + //Read to throw out newline at end of header + readHeaderElement(conversionInFile, 1); } -bool SessionRecording::convertFile(std::string filename, std::string version) { +bool SessionRecording::convertFile(std::string filename, int depth, std::string version) { bool success = true; std::string conversionInFilename; std::ifstream conversionInFile; @@ -1720,6 +1728,11 @@ bool SessionRecording::convertFile(std::string filename, std::string version) { bool preRecursion = (version == "root") ? true : false; std::string throwOut; + if (depth == _maximumRecursionDepth) { + LERROR("Runaway recursion in session recording conversion of file version."); + exit(EXIT_FAILURE); + } + try { readPlaybackFileHeader( filename, @@ -1729,19 +1742,15 @@ bool SessionRecording::convertFile(std::string filename, std::string version) { mode ); int conversionLineNum = 1; - LINFO(fmt::format( - "Starting conversion on rec file {}, version {} in {} mode.", - filename, version, (mode == DataMode::Ascii) ? "ascii" : "binary" - )); //If this instance of the SessionRecording class isn't the instance with the // correct version of the file to be converted, then call getLegacy() to recurse // to the next level down in the legacy subclasses until we get the right // version, then proceed with conversion from there. - if (version.compare(FileHeaderVersion) != 0) { + if (version.compare(fileFormatVersion()) != 0) { conversionInFile.close(); - SessionRecording* old = getLegacy(); - old->convertFile(filename, version); + getLegacy(); + _legacyVersion->convertFile(filename, depth + 1, version); readPlaybackFileHeader( filename, conversionInFilename, @@ -1751,43 +1760,48 @@ bool SessionRecording::convertFile(std::string filename, std::string version) { ); } - if (!conversionInFile.is_open() || !conversionInFile.good()) { - throw ConversionError(fmt::format( - "Unable to open file {} for conversion", conversionInFilename.c_str() + if (version != "root") { + if (!conversionInFile.is_open() || !conversionInFile.good()) { + throw ConversionError(fmt::format( + "Unable to open file {} for conversion", conversionInFilename.c_str() + )); + } + std::string conversionOutFilename = determineConversionOutFilename(filename); + LINFO(fmt::format( + "Starting conversion on rec file {}, version {} in {} mode. " + "Writing result to {}.", + filename, version, (mode == DataMode::Ascii) ? "ascii" : "binary", + conversionOutFilename )); + std::ofstream conversionOutFile; + if (mode == DataMode::Binary) { + conversionOutFile.open(conversionOutFilename, std::ios::binary); + } + else { + conversionOutFile.open(conversionOutFilename); + } + if (!conversionOutFile.is_open() || !conversionOutFile.good()) { + LERROR(fmt::format( + "Unable to open file {} for conversion result", + conversionOutFilename.c_str() + )); + return false; + } + success = convertEntries( + conversionInFilename, + conversionInFile, + mode, + conversionLineNum, + conversionOutFile + ); + conversionInFile.close(); + conversionOutFile.close(); } - - std::string conversionOutFilename = determineConversionOutFilename(filename); - std::ofstream conversionOutFile; - if (mode == DataMode::Binary) { - conversionOutFile.open(conversionOutFilename, std::ios::binary); - } - else { - conversionOutFile.open(conversionOutFilename); - } - if (!conversionOutFile.is_open() || !conversionOutFile.good()) { - LERROR(fmt::format( - "Unable to open file {} for conversion result", - conversionOutFilename.c_str() - )); - return false; - } - - success = convertEntries( - conversionInFilename, - conversionInFile, - mode, - conversionLineNum, - conversionOutFile - ); - conversionInFile.close(); - conversionOutFile.close(); } catch (ConversionError& c) { LERROR(c.message); success = false; } - return success; } @@ -1922,24 +1936,36 @@ bool SessionRecording::convertEntries(std::string& inFilename, std::ifstream& in return conversionStatusOk; } -SessionRecording* SessionRecording::getLegacy() { - legacyVersion = new SessionRecording_legacy_0085(); +void SessionRecording::getLegacy() { + _legacyVersion = new SessionRecording_legacy_0085(); +} + +std::string SessionRecording::fileFormatVersion() { + return std::string(FileHeaderVersion); } std::string SessionRecording::determineConversionOutFilename(const std::string filename) { + const std::string legacyRecordingSaveDirectory = "${RECORDINGS}/convert"; std::string conversionOutFilename; if (filename.substr(filename.find_last_of(".")) == FileExtensionBinary) { conversionOutFilename = filename.substr(0, filename.find_last_of(".")) - + "_" + FileHeaderVersion + FileExtensionBinary; + + "_" + fileFormatVersion() + FileExtensionBinary; } else if (filename.substr(filename.find_last_of(".")) == FileExtensionAscii) { conversionOutFilename = filename.substr(0, filename.find_last_of(".")) - + "_" + FileHeaderVersion + FileExtensionAscii; + + "_" + fileFormatVersion() + FileExtensionAscii; } else { conversionOutFilename = filename + "_"; } - return absPath("${RECORDINGS}/convert/" + conversionOutFilename); + //if (!FileSys.directoryExists(absPath(legacyRecordingSaveDirectory))) { + // FileSys.createDirectory( + // absPath(legacyRecordingSaveDirectory), + // ghoul::filesystem::FileSystem::Recursive::Yes + // ); + //} + //return absPath(legacyRecordingSaveDirectory + "/" + conversionOutFilename); + return absPath(conversionOutFilename); } bool SessionRecording_legacy_0085::convertScript(std::ifstream& inFile, DataMode mode, diff --git a/src/interaction/sessionrecording_lua.inl b/src/interaction/sessionrecording_lua.inl index 79c59889b7..494012d75a 100644 --- a/src/interaction/sessionrecording_lua.inl +++ b/src/interaction/sessionrecording_lua.inl @@ -180,7 +180,7 @@ int fileFormatConversion(lua_State* L) { return luaL_error(L, "filepath string is empty"); } - global::sessionRecording.convertFile(convertFilePath); + global::sessionRecording.convertFile(convertFilePath, 0); ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack"); return 0; diff --git a/src/interaction/tasks/convertrecfileversiontask.cpp b/src/interaction/tasks/convertrecfileversiontask.cpp new file mode 100644 index 0000000000..8487bbe634 --- /dev/null +++ b/src/interaction/tasks/convertrecfileversiontask.cpp @@ -0,0 +1,126 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2020 * + * * + * 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 + +namespace { + constexpr const char* _loggerCat = "ConvertRecFileVersionTask"; + + constexpr const char* KeyInFilePath = "InputFilePath"; +} // namespace + +namespace openspace::interaction { + +ConvertRecFileVersionTask::ConvertRecFileVersionTask(const ghoul::Dictionary& dictionary) +{ + openspace::documentation::testSpecificationAndThrow( + documentation(), + dictionary, + "ConvertRecFileVersionTask" + ); + + _inFilename = dictionary.value(KeyInFilePath); + _inFilePath = absPath(_inFilename); + + std::string::size_type idx = _inFilename.find_last_of('/'); + if( idx != std::string::npos ) { + _inFilename = _inFilename.substr(idx + 1); + } + + ghoul_assert(FileSys.fileExists(_inFilePath), "The filename must exist"); + if (!FileSys.fileExists(_inFilePath)) { + LERROR(fmt::format("Failed to load session recording file: {}", _inFilePath)); + } + else { + sessRec = new SessionRecording(false); + } +} + +ConvertRecFileVersionTask::~ConvertRecFileVersionTask() { + if (sessRec != nullptr) { + delete sessRec; + } +} + +std::string ConvertRecFileVersionTask::description() { + std::string description = "Convert file format of session recording file '" + + _inFilePath + "' to current version."; + return description; +} + +void ConvertRecFileVersionTask::perform(const Task::ProgressCallback& progressCallback) { + convert(); +} + +void ConvertRecFileVersionTask::convert() { + bool hasBinaryFileExtension = sessRec->hasFileExtension( + _inFilename, + SessionRecording::FileExtensionBinary + ); + bool hasAsciiFileExtension = sessRec->hasFileExtension( + _inFilename, + SessionRecording::FileExtensionAscii + ); + if (!hasBinaryFileExtension && !hasAsciiFileExtension) { + LERROR(fmt::format( + "Input filename does not have expected {} or {} extension.", + SessionRecording::FileExtensionBinary, + SessionRecording::FileExtensionAscii + )); + return; + } + sessRec->convertFile(_inFilename, 0); +} + +documentation::Documentation ConvertRecFileVersionTask::documentation() { + using namespace documentation; + return { + "ConvertRecFileVersionTask", + "convert_file_version_task", + { + { + "Type", + new StringEqualVerifier("ConvertRecFileVersionTask"), + Optional::No, + "The type of this task", + }, + { + KeyInFilePath, + new StringAnnotationVerifier("A valid filename to convert"), + Optional::No, + "The filename to update to the current file format.", + }, + }, + }; +} + +}