diff --git a/Engine/Source/Runtime/Core/Private/Core/Assertion/Assertion.cpp b/Engine/Source/Runtime/Core/Private/Core/Assertion/Assertion.cpp new file mode 100644 index 0000000..ff5cb35 --- /dev/null +++ b/Engine/Source/Runtime/Core/Private/Core/Assertion/Assertion.cpp @@ -0,0 +1,39 @@ +// RavenStorm Copyright @ 2025-2025 + +#include "Core/Assertion/Assertion.hpp" + +#include +#include + +#include "Core/Assertion/ExceptionHandler.hpp" +#include "Core/Logging/LogManager.hpp" + +DEFINE_LOG_CHANNEL(Assert, All) + +void Assertion::Assert(FString&& Condition, FString&& Message, const long CustomCode, const FSourceLocation& Location) +{ +#ifdef CORVUS_MODE_DEBUG + if (IsDebuggerPresent()) + { + __debugbreak(); + } +#endif + FExceptionMetadata Metadata; + Metadata.ReportCode = ERROR_CODE_ASSERTION; + Metadata.AssertionCondition = std::move(Condition); + Metadata.Message = std::move(Message); + Metadata.SourceLocation = Location; + Metadata.CustomCode = CustomCode; + FExceptionHandler::Report(Metadata); +} + +void Assertion::Assert(const FExceptionMetadata& Metadata) +{ +#ifdef CORVUS_MODE_DEBUG + if (IsDebuggerPresent()) + { + __debugbreak(); + } +#endif + FExceptionHandler::Report(Metadata); +} diff --git a/Engine/Source/Runtime/Core/Private/Core/Assertion/ExceptionHandler.cpp b/Engine/Source/Runtime/Core/Private/Core/Assertion/ExceptionHandler.cpp new file mode 100644 index 0000000..4466ff5 --- /dev/null +++ b/Engine/Source/Runtime/Core/Private/Core/Assertion/ExceptionHandler.cpp @@ -0,0 +1,102 @@ +// RavenStorm Copyright @ 2025-2025 + +#include "Core/Assertion/ExceptionHandler.hpp" + +#include +#include + +#include "Core/Logging/LogManager.hpp" + +int32 FExceptionHandler::ExitCode = EXIT_SUCCESS; + +FString FormatSourceLocation(const FSourceLocation& SourceLocation) +{ + FString Result; + FString FileName = SourceLocation.file_name(); + const size64 LastSlashIndex = FileName.find_last_of('\\'); + if (LastSlashIndex != FString::npos) + { + FileName = FileName.substr(LastSlashIndex + 1); + } + Result += "File: " + FileName + "\n"; + Result += "Line: " + std::to_string(SourceLocation.line()) + "\n"; + Result += "Function: " + FString(SourceLocation.function_name()) + "\n"; + return Result; +} + +long FExceptionHandler::HandleException(const EXCEPTION_POINTERS* ExceptionInfo) +{ + ExitCode = EXIT_FAILURE; + + const HRESULT ExceptionCode = static_cast(ExceptionInfo->ExceptionRecord->ExceptionCode); + + FString MessageBoxCaption; + FString MessageBoxText; + uint32 MessageBoxType; + if (ExceptionCode == ERROR_CODE_ASSERTION) + { + const FExceptionMetadata* Metadata = reinterpret_cast(ExceptionInfo->ExceptionRecord->ExceptionInformation[0]); + + MessageBoxCaption = "Corvus | Assertion"; + MessageBoxText = "Assertion Failure!\n\n"; + if (Metadata->Message.empty() && Metadata->CustomCode != 0) + { + MessageBoxText += GetResultDescription(Metadata->CustomCode) + "\n\n"; + } + else + { + MessageBoxText += Metadata->Message + "\n\n"; + } + MessageBoxText += "Condition: " + Metadata->AssertionCondition + "\n"; + MessageBoxText += FormatSourceLocation(Metadata->SourceLocation); + if (!Metadata->Message.empty() && Metadata->CustomCode != 0) + { + MessageBoxText += "Result: " + GetResultDescription(Metadata->CustomCode) + "\n"; + } + MessageBoxType = MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_SETFOREGROUND | MB_TOPMOST; + } + else + { + MessageBoxCaption = "Corvus | Exception"; + MessageBoxText = "Unhandled Exception!\n\n"; + MessageBoxText += "Result: " + GetResultDescription(ExceptionCode) + "\n"; + MessageBoxType = MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_SETFOREGROUND | MB_TOPMOST; + } + MessageBoxEx(nullptr, MessageBoxText.c_str(), MessageBoxCaption.c_str(), MessageBoxType, MAKELANGID(LANG_NEUTRAL, SUBLANG_ENGLISH_US)); + return EXCEPTION_EXECUTE_HANDLER; +} + +void FExceptionHandler::Report(const FExceptionMetadata& Metadata) +{ + ULONG_PTR Parameters[] = {reinterpret_cast(&Metadata)}; + RaiseException(Metadata.ReportCode, EXCEPTION_NONCONTINUABLE, _countof(Parameters), Parameters); +} + +FString FExceptionHandler::GetResultDescription(const HRESULT Result) +{ + if (HRESULT_FACILITY(Result) == FACILITY_ITF) + { + return "Unknown"; + } + LPSTR Buffer = nullptr; + const size64 Size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, Result, + MAKELANGID(LANG_NEUTRAL, SUBLANG_ENGLISH_US), reinterpret_cast(&Buffer), 0, nullptr); + FString Message(Buffer, Size); + LocalFree(Buffer); + if (!Message.empty()) + { + Message = Message.substr(0, Message.size() - 2); + return Message; + } + return {}; +} + +int32 FExceptionHandler::GetExitCode() +{ + return ExitCode; +} + +long HandleException(const EXCEPTION_POINTERS* ExceptionInfo) +{ + return FExceptionHandler::HandleException(ExceptionInfo); +} diff --git a/Engine/Source/Runtime/Core/Public/Core/Assertion/Assertion.hpp b/Engine/Source/Runtime/Core/Public/Core/Assertion/Assertion.hpp new file mode 100644 index 0000000..5fbfbeb --- /dev/null +++ b/Engine/Source/Runtime/Core/Public/Core/Assertion/Assertion.hpp @@ -0,0 +1,35 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include "Core/Containers/String.hpp" +#include "Core/Logging/LogChannel.hpp" +#include "Core/Utility/SourceLocation.hpp" +#include "Core/Utility/StringUtils.hpp" + +struct FExceptionMetadata; +CORE_API DECLARE_LOG_CHANNEL_EXTERN(Assert) + +namespace Assertion +{ + CORE_API void Assert(FString&& Condition, FString&& Message = "", long CustomCode = 0, const FSourceLocation& Location = FSourceLocation::current()); + CORE_API void Assert(const FExceptionMetadata& Metadata); +} + +#define ASSERT_IF(Condition, ...) \ + do \ + { \ + if(!(Condition)) \ + { \ + Assertion::Assert(FExceptionMetadata{ .AssertionCondition = FString(TEXT(#Condition)) __VA_OPT__(, .Message = StringUtils::Format(__VA_ARGS__)), .SourceLocation = FSourceLocation::current() }); \ + } \ + } while (false) + +#define ASSERT_RESULT(Result, ...) \ + do \ + { \ + if(FAILED(Result)) \ + { \ + Assertion::Assert(FExceptionMetadata{ .ReportCode = ERROR_CODE_ASSERTION, .AssertionCondition = FString(TEXT(#Result)) __VA_OPT__(, .Message = StringUtils::Format(__VA_ARGS__)), .CustomCode = Result, .SourceLocation = FSourceLocation::current() }); \ + } \ + } while (false) diff --git a/Engine/Source/Runtime/Core/Public/Core/Assertion/ExceptionHandler.hpp b/Engine/Source/Runtime/Core/Public/Core/Assertion/ExceptionHandler.hpp new file mode 100644 index 0000000..4df8016 --- /dev/null +++ b/Engine/Source/Runtime/Core/Public/Core/Assertion/ExceptionHandler.hpp @@ -0,0 +1,38 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include + +#include "Core/Containers/String.hpp" +#include "Core/Utility/SourceLocation.hpp" + +#define ERROR_CODE_UNKNOWN MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x0001) +#define ERROR_CODE_ASSERTION MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x0002) + +struct CORE_API FExceptionMetadata +{ + HRESULT ReportCode = ERROR_CODE_UNKNOWN; + FString AssertionCondition; // Only valid if report code is ERROR_CODE_ASSERTION + FString Message; + HRESULT CustomCode = 0; + FSourceLocation SourceLocation; +}; + +class CORE_API FExceptionHandler +{ +public: + [[nodiscard]] static long HandleException(const EXCEPTION_POINTERS* ExceptionInfo); + static void Report(const FExceptionMetadata& Metadata); + [[nodiscard]] static FString GetResultDescription(HRESULT Result); + + [[nodiscard]] static int32 GetExitCode(); + +private: + static int32 ExitCode; +}; + +[[nodiscard]] CORE_API long HandleException(const EXCEPTION_POINTERS* ExceptionInfo); + +#define TRY __try +#define CATCH() __except(HandleException(GetExceptionInformation())) diff --git a/Engine/Source/Runtime/Core/Public/Core/Utility/EnumFlags.hpp b/Engine/Source/Runtime/Core/Public/Core/Utility/EnumFlags.hpp new file mode 100644 index 0000000..1c01000 --- /dev/null +++ b/Engine/Source/Runtime/Core/Public/Core/Utility/EnumFlags.hpp @@ -0,0 +1,117 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include + +#define REGISTER_AS_ENUM_FLAG(EnumName) \ + template<> \ + [[nodiscard]] constexpr bool8 IsEnumFlag(EnumName) { return true; } + +template +[[nodiscard]] constexpr bool8 IsEnumFlag(T) +{ + return false; +} + +template +concept CIsUnsignedEnum = std::is_enum_v && std::is_unsigned_v>; + +template +concept CIsEnumFlag = CIsUnsignedEnum && IsEnumFlag(T{}); + +template +[[nodiscard]] bool8 HasEnumFlag(T Value, T Flag) +{ + return (Value & Flag) == Flag; +} + +template +[[nodiscard]] bool8 HasEnumFlag(T Value, std::underlying_type_t Flag) +{ + return (static_cast>(Value) & Flag) == Flag; +} + +template +[[nodiscard]] bool8 HasEnumFlag(std::underlying_type_t Value, T Flag) +{ + return (Value & static_cast>(Flag)) == static_cast>(Flag); +} + +template requires CIsEnumFlag +constexpr T operator~(T Value) +{ + return static_cast(~static_cast>(Value)); +} + +template requires CIsEnumFlag +constexpr auto operator|(T Left, T Right) +{ + return static_cast(static_cast>(Left) | static_cast>(Right)); +} + +template requires CIsEnumFlag +constexpr auto operator|(std::underlying_type_t Left, T Right) +{ + return static_cast>(Left | static_cast>(Right)); +} + +template requires CIsEnumFlag +constexpr auto operator|(T Left, std::underlying_type_t Right) +{ + return static_cast(static_cast>(Left) | Right); +} + +template requires CIsEnumFlag +constexpr auto operator|(std::underlying_type_t& Left, const T Right) +{ + return Left = Left | Right; +} + +template requires CIsEnumFlag +constexpr auto operator&(T Left, T Right) +{ + return static_cast(static_cast>(Left) & static_cast>(Right)); +} + +template requires CIsEnumFlag +constexpr auto operator&(std::underlying_type_t Left, T Right) +{ + return static_cast>(Left & static_cast>(Right)); +} + +template requires CIsEnumFlag +constexpr auto operator&(T Left, std::underlying_type_t Right) +{ + return static_cast(static_cast>(Left) & Right); +} + +template requires CIsEnumFlag +constexpr auto operator&=(std::underlying_type_t& Left, const T Right) +{ + return Left = Left & Right; +} + +template requires CIsEnumFlag +constexpr auto operator^(T Left, T Right) +{ + return static_cast(static_cast>(Left) ^ static_cast>(Right)); +} + +template requires CIsEnumFlag +constexpr auto operator^(std::underlying_type_t Left, T Right) +{ + return static_cast>(Left ^ static_cast>(Right)); +} + +template requires CIsEnumFlag +constexpr auto operator^(T Left, std::underlying_type_t Right) +{ + return static_cast(static_cast>(Left) ^ Right); +} + +template requires CIsEnumFlag +constexpr auto operator^=(std::underlying_type_t& Left, const T Right) +{ + return Left = Left ^ Right; +} diff --git a/Engine/Source/Runtime/Core/Public/Core/Utility/SourceLocation.hpp b/Engine/Source/Runtime/Core/Public/Core/Utility/SourceLocation.hpp new file mode 100644 index 0000000..8b38426 --- /dev/null +++ b/Engine/Source/Runtime/Core/Public/Core/Utility/SourceLocation.hpp @@ -0,0 +1,7 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include + +using FSourceLocation = std::source_location;