Skip to content

Commit

Permalink
Oss release v1.2.2 created 2022-10-05-09-56
Browse files Browse the repository at this point in the history
see CHANGELOG.md for details

Original commit sha: 340e202f98b3c88d432a6d35dce95908f5450214

Co-authored-by: Askanaz Torosyan <46795157+nVxx@users.noreply.github.com>
Co-authored-by: Daniel Haas <25718295+bojackHaasman@users.noreply.github.com>
Co-authored-by: Mohamed Sharaf-El-Deen <769940+mohhsharaf@users.noreply.github.com>
Co-authored-by: Mirko Sova <64351017+smirko-dev@users.noreply.github.com>
Co-authored-by: Tobias Hammer <tohammer@users.noreply.github.com>
Co-authored-by: Violin Yanev <violinyanev@users.noreply.github.com>
  • Loading branch information
7 people committed Oct 5, 2022
1 parent e648e0d commit 7c65de5
Show file tree
Hide file tree
Showing 13 changed files with 240 additions and 15 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@

# Unreleased

**CHANGED**
# v1.2.2

**ADDED**

* Added LogicEngine::loadFromFileDescriptor() to load logic files with an open file descriptor

**FIXED**

* Scripts (and other nodes) with no input or no output properties do not fail validation anymore,
only nodes with actual properties on each input/output side are checked if they are linked

# v1.2.1

**FIXED**
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ cmake_minimum_required(VERSION 3.13)

set(RLOGIC_VERSION_MAJOR 1)
set(RLOGIC_VERSION_MINOR 2)
set(RLOGIC_VERSION_PATCH 1)
set(RLOGIC_VERSION_PATCH 2)

set(RLOGIC_VERSION ${RLOGIC_VERSION_MAJOR}.${RLOGIC_VERSION_MINOR}.${RLOGIC_VERSION_PATCH})
set(ramses-logic_VERSION "${RLOGIC_VERSION}" CACHE STRING "Ramses Logic version" FORCE)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ existing files exported with Logic Engine version **W** or newer (Binary file co

|Logic | Included Ramses version | Minimum required Ramses version | Binary file compatibility |
|----------|-------------------------------|------------------------------------|------------------------------|
|v1.2.2 | 27.0.125 | 27.0.102 | >= 1.0.0, F-Levels 01 - 03 |
|v1.2.1 | 27.0.125 | 27.0.102 | >= 1.0.0, F-Levels 01 - 03 |
|v1.2.0 | 27.0.122 | 27.0.102 | >= 1.0.0, F-Levels 01 - 03 |
|v1.1.x | 27.0.121 | 27.0.102 | >= 1.0.0, F-Levels 01, 02 |
Expand Down
17 changes: 17 additions & 0 deletions include/ramses-logic/LogicEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,23 @@ namespace rlogic
*/
RLOGIC_API bool loadFromFile(std::string_view filename, ramses::Scene* ramsesScene = nullptr, bool enableMemoryVerification = true);

/**
* Loads the whole LogicEngine data from the given file descriptor. This method is equivalent to #loadFromFile().
*
* The file descriptor must be opened for read access and must support seeking.
* It will be in closed state after this call.
*
* @param[in] fd Open and readable filedescriptor.
* @param[in] offset Absolute starting position of LogicEngine data within fd.
* @param[in] length Size of the data within fd.
* @param ramsesScene pointer to the Ramses Scene which holds the objects referenced in the Ramses Logic file
* @param enableMemoryVerification flag to enable memory verifier (a flatbuffers feature which checks bounds and ranges).
* Disable this only if the file comes from a trusted source and performance is paramount.
* @return true if deserialization was successful, false otherwise. To get more detailed
* error information use #getErrors()
*/
RLOGIC_API bool loadFromFileDescriptor(int fd, size_t offset, size_t length, ramses::Scene* ramsesScene = nullptr, bool enableMemoryVerification = true);

/**
* Loads the whole LogicEngine data from the given memory buffer. This method is equivalent to
* #loadFromFile but allows to have the file-opening
Expand Down
5 changes: 5 additions & 0 deletions lib/impl/LogicEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@ namespace rlogic
return m_impl->loadFromFile(filename, ramsesScene, enableMemoryVerification);
}

bool LogicEngine::loadFromFileDescriptor(int fd, size_t offset, size_t length, ramses::Scene* ramsesScene /* = nullptr*/, bool enableMemoryVerification /* = true */)
{
return m_impl->loadFromFileDescriptor(fd, offset, length, ramsesScene, enableMemoryVerification);
}

bool LogicEngine::loadFromBuffer(const void* rawBuffer, size_t bufferSize, ramses::Scene* ramsesScene /* = nullptr*/, bool enableMemoryVerification /* = true */)
{
return m_impl->loadFromBuffer(rawBuffer, bufferSize, ramsesScene, enableMemoryVerification);
Expand Down
21 changes: 21 additions & 0 deletions lib/impl/LogicEngineImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,27 @@ namespace rlogic::internal
return loadFromByteData((*maybeBytesFromFile).data(), fileSize, scene, enableMemoryVerification, fmt::format("file '{}' (size: {})", filename, fileSize));
}

bool LogicEngineImpl::loadFromFileDescriptor(int fd, size_t offset, size_t size, ramses::Scene* scene, bool enableMemoryVerification)
{
if (fd <= 0)
{
m_errors.add(fmt::format("Invalid file descriptor: {}", fd), nullptr, EErrorType::BinaryDataAccessError);
return false;
}
if (size == 0)
{
m_errors.add("Failed to load from file descriptor: size may not be 0", nullptr, EErrorType::BinaryDataAccessError);
return false;
}
std::optional<std::vector<char>> maybeBytesFromFile = FileUtils::LoadBinary(fd, offset, size);
if (!maybeBytesFromFile)
{
m_errors.add(fmt::format("Failed to load from file descriptor: fd: {} offset: {} size: {}", fd, offset, size), nullptr, EErrorType::BinaryDataAccessError);
return false;
}
return loadFromByteData((*maybeBytesFromFile).data(), size, scene, enableMemoryVerification, fmt::format("fd: {} (offset: {}, size: {})", fd, offset, size));
}

bool LogicEngineImpl::checkFileIdentifierBytes(const std::string& dataSourceDescription, const std::string& fileIdBytes)
{
const std::string expected = getFileIdentifierMatchingFeatureLevel();
Expand Down
1 change: 1 addition & 0 deletions lib/impl/LogicEngineImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ namespace rlogic::internal
const std::vector<WarningData>& validate() const;

bool loadFromFile(std::string_view filename, ramses::Scene* scene, bool enableMemoryVerification);
bool loadFromFileDescriptor(int fd, size_t offset, size_t size, ramses::Scene* scene, bool enableMemoryVerification);
bool loadFromBuffer(const void* rawBuffer, size_t bufferSize, ramses::Scene* scene, bool enableMemoryVerification);
bool saveToFile(std::string_view filename, const SaveFileConfigImpl& config);
[[nodiscard]] static bool GetFeatureLevelFromFile(std::string_view filename, EFeatureLevel& detectedFeatureLevel);
Expand Down
30 changes: 18 additions & 12 deletions lib/internals/ApiObjects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -736,13 +736,16 @@ namespace rlogic::internal
continue;

assert(objAsNode->getOutputs() != nullptr);
// only check for unlinked outputs if there are any
if (objAsNode->getOutputs()->getChildCount() != 0u)
{
bool anyOutputLinked = false;
for (const auto* output : objAsNode->getOutputs()->m_impl->collectLeafChildren())
anyOutputLinked |= (output->hasOutgoingLink());

bool anyOutputLinked = false;
for(const auto* output : objAsNode->getOutputs()->m_impl->collectLeafChildren())
anyOutputLinked |= (output->hasOutgoingLink());

if (!anyOutputLinked)
validationResults.add(fmt::format("Node [{}] has no outgoing links! Node should be deleted or properly linked!", objAsNode->getName()), objAsNode, EWarningType::UnusedContent);
if (!anyOutputLinked)
validationResults.add(fmt::format("Node [{}] has no outgoing links! Node should be deleted or properly linked!", objAsNode->getName()), objAsNode, EWarningType::UnusedContent);
}
}
}

Expand All @@ -760,13 +763,16 @@ namespace rlogic::internal
continue;

assert(objAsNode->getInputs() != nullptr);
// only check for unlinked inputs if there are any
if (objAsNode->getInputs()->getChildCount() != 0u)
{
bool anyInputLinked = false;
for (const auto* input : objAsNode->getInputs()->m_impl->collectLeafChildren())
anyInputLinked |= (input->hasIncomingLink());

bool anyInputLinked = false;
for (const auto* input : objAsNode->getInputs()->m_impl->collectLeafChildren())
anyInputLinked |= (input->hasIncomingLink());

if (!anyInputLinked)
validationResults.add(fmt::format("Node [{}] has no ingoing links! Node should be deleted or properly linked!", objAsNode->getName()), objAsNode, EWarningType::UnusedContent);
if (!anyInputLinked)
validationResults.add(fmt::format("Node [{}] has no ingoing links! Node should be deleted or properly linked!", objAsNode->getName()), objAsNode, EWarningType::UnusedContent);
}
}
}
}
Expand Down
24 changes: 24 additions & 0 deletions lib/internals/FileUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,28 @@ namespace rlogic::internal

return byteBuffer;
}

std::optional<std::vector<char>> FileUtils::LoadBinary(int fd, size_t offset, size_t size)
{
std::unique_ptr<FILE, decltype(&fclose)> file(fdopen(fd, "rb"), &fclose);
if (!file)
{
return std::nullopt;
}

// NOLINTNEXTLINE(google-runtime-int) long cast required to match with fseek declaration
if (fseek(file.get(), static_cast<long>(offset), SEEK_SET) != 0)
{
return std::nullopt;
}

std::vector<char> byteBuffer(size);

const auto bytesRead = fread(byteBuffer.data(), 1, size, file.get());
if (bytesRead != size)
{
return std::nullopt;
}
return byteBuffer;
}
}
1 change: 1 addition & 0 deletions lib/internals/FileUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ namespace rlogic::internal
public:
static bool SaveBinary(const std::string& filename, const void* binaryBuffer, size_t bufferLength);
static std::optional<std::vector<char>> LoadBinary(const std::string& filename);
static std::optional<std::vector<char>> LoadBinary(int fd, size_t offset, size_t size);
};
}
53 changes: 53 additions & 0 deletions unittests/api/LogicEngineTest_Serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "internals/ApiObjects.h"
#include "internals/FileUtils.h"
#include "LogTestUtils.h"
#include "FileDescriptorHelper.h"

#include "generated/LogicEngineGen.h"
#include "ramses-logic-build-config.h"
Expand Down Expand Up @@ -214,6 +215,58 @@ namespace rlogic::internal
EXPECT_EQ("Failed to load file 'folder'", m_logicEngine.getErrors()[0].message);
}

TEST_P(ALogicEngine_Serialization, LoadFromFileDescriptor)
{
std::vector<char> bufferData = CreateTestBuffer();
SaveBufferToFile(bufferData, "LogicEngine.bin");
const int fd = FileDescriptorHelper::OpenFileDescriptorBinary("LogicEngine.bin");
EXPECT_LT(0, fd);
EXPECT_TRUE(m_logicEngine.loadFromFileDescriptor(fd, 0, bufferData.size()));
}

TEST_P(ALogicEngine_Serialization, LoadFromFileDescriptorWithOffset)
{
const size_t offset = 10;
std::vector<char> bufferData = CreateTestBuffer();
bufferData.insert(bufferData.begin(), offset, 'x');
SaveBufferToFile(bufferData, "LogicEngine.bin");
const int fd = FileDescriptorHelper::OpenFileDescriptorBinary("LogicEngine.bin");
EXPECT_LT(0, fd);
EXPECT_TRUE(m_logicEngine.loadFromFileDescriptor(fd, offset, bufferData.size()- offset));
}

TEST_P(ALogicEngine_Serialization, LoadFromFileDescriptor_InvalidFileDescriptor)
{
EXPECT_FALSE(m_logicEngine.loadFromFileDescriptor(0, 0, 1000));
EXPECT_EQ("Invalid file descriptor: 0", m_logicEngine.getErrors()[0].message);
}

TEST_P(ALogicEngine_Serialization, LoadFromFileDescriptor_ZeroSize)
{
EXPECT_FALSE(m_logicEngine.loadFromFileDescriptor(42, 0, 0));
EXPECT_EQ("Failed to load from file descriptor: size may not be 0", m_logicEngine.getErrors()[0].message);
}

TEST_P(ALogicEngine_Serialization, LoadFromFileDescriptor_InvalidOffset)
{
std::vector<char> bufferData = CreateTestBuffer();
SaveBufferToFile(bufferData, "LogicEngine.bin");
const int fd = FileDescriptorHelper::OpenFileDescriptorBinary("LogicEngine.bin");
EXPECT_FALSE(m_logicEngine.loadFromFileDescriptor(fd, bufferData.size(), bufferData.size()));
EXPECT_EQ(fmt::format("Failed to load from file descriptor: fd: {} offset: {} size: {}", fd, bufferData.size(), bufferData.size()),
m_logicEngine.getErrors()[0].message);
}

TEST_P(ALogicEngine_Serialization, LoadFromFileDescriptor_InvalidSize)
{
std::vector<char> bufferData = CreateTestBuffer();
SaveBufferToFile(bufferData, "LogicEngine.bin");
const int fd = FileDescriptorHelper::OpenFileDescriptorBinary("LogicEngine.bin");
EXPECT_FALSE(m_logicEngine.loadFromFileDescriptor(fd, 0, bufferData.size() + 1));
EXPECT_EQ(fmt::format("Failed to load from file descriptor: fd: {} offset: {} size: {}", fd, 0, bufferData.size() + 1),
m_logicEngine.getErrors()[0].message);
}

TEST_P(ALogicEngine_Serialization, DeserializesFromMemoryBuffer)
{
const std::vector<char> bufferData = CreateTestBuffer();
Expand Down
61 changes: 60 additions & 1 deletion unittests/internal/ApiObjectsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1224,8 +1224,9 @@ namespace rlogic::internal
TEST_P(AnApiObjects, ValidatesDanglingNodes_ProducesWarningIfNodeHasNoIngoingOrOutgoingLinks)
{
auto* script = m_apiObjects.createLuaScript(R"(
function interface(IN)
function interface(IN, OUT)
IN.param1 = Type:Int32()
OUT.param1 = Type:Int32()
end
function run(IN,OUT)
end
Expand All @@ -1240,6 +1241,64 @@ namespace rlogic::internal
EXPECT_THAT(validationResults.getWarnings(), ::testing::Each(::testing::Field(&WarningData::type, ::testing::Eq(EWarningType::UnusedContent))));
}

TEST_P(AnApiObjects, ValidatesDanglingNodes_DoesNotProduceWarningIfNodeHasNoInputs)
{
const auto script = m_apiObjects.createLuaScript(R"(
function interface(IN,OUT)
OUT.param1 = Type:Int32()
end
function run(IN,OUT)
end
)", {}, "script name", m_errorReporting);
ASSERT_NE(nullptr, script);

const auto dummyInputScript = m_apiObjects.createLuaScript(R"LUA_SCRIPT(
function interface(IN)
IN.param1 = Type:Int32()
end
function run(IN,OUT)
end
)LUA_SCRIPT", {}, "dummy script", m_errorReporting);
ASSERT_NE(nullptr, dummyInputScript);

// link script's output in order to pass outputs validation
m_apiObjects.getLogicNodeDependencies().link(*script->getOutputs()->getChild(0u)->m_impl, *dummyInputScript->getInputs()->getChild(0u)->m_impl, false, m_errorReporting);

ValidationResults validationResults;
m_apiObjects.validateDanglingNodes(validationResults);
EXPECT_TRUE(validationResults.getWarnings().empty());
}

TEST_P(AnApiObjects, ValidatesDanglingNodes_DoesNotProduceWarningIfNodeHasNoOutputs)
{
const auto script = m_apiObjects.createLuaScript(R"(
function interface(IN,OUT)
IN.param1 = Type:Int32()
end
function run(IN,OUT)
end
)", {}, "script name", m_errorReporting);
ASSERT_NE(nullptr, script);

const auto dummyOutputScript = m_apiObjects.createLuaScript(R"LUA_SCRIPT(
function interface(IN,OUT)
OUT.param1 = Type:Int32()
end
function run(IN,OUT)
end
)LUA_SCRIPT", {}, "dummy script", m_errorReporting);
ASSERT_NE(nullptr, dummyOutputScript);

// link script's input in order to pass inputs validation
m_apiObjects.getLogicNodeDependencies().link(*dummyOutputScript->getOutputs()->getChild(0u)->m_impl, *script->getInputs()->getChild(0u)->m_impl, false, m_errorReporting);

ValidationResults validationResults;
m_apiObjects.validateDanglingNodes(validationResults);
EXPECT_TRUE(validationResults.getWarnings().empty());
}

class AnApiObjects_SceneMismatch : public AnApiObjects
{
protected:
Expand Down
32 changes: 32 additions & 0 deletions unittests/shared/FileDescriptorHelper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// -------------------------------------------------------------------------
// Copyright (C) 2022 BMW AG
// -------------------------------------------------------------------------
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
// -------------------------------------------------------------------------

#pragma once

#include <fcntl.h>
#if _WIN32
# include <io.h>
#else
# include <unistd.h>
#endif

namespace rlogic
{
namespace FileDescriptorHelper
{
inline int OpenFileDescriptorBinary(const char* path, int flags = O_RDONLY)
{
#if _WIN32
return ::open(path, flags | O_BINARY);
#else
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) system call
return ::open(path, flags);
#endif
}
}
}

0 comments on commit 7c65de5

Please sign in to comment.