diff --git a/SHADE_Engine/SHADE_Engine.vcxproj b/SHADE_Engine/SHADE_Engine.vcxproj
index b7f87645..fbb9599b 100644
--- a/SHADE_Engine/SHADE_Engine.vcxproj
+++ b/SHADE_Engine/SHADE_Engine.vcxproj
@@ -187,15 +187,24 @@
+
+
+
+
+
+
+
+
+
@@ -262,9 +271,12 @@
Create
+
+
+
diff --git a/SHADE_Engine/SHADE_Engine.vcxproj.filters b/SHADE_Engine/SHADE_Engine.vcxproj.filters
index fd513c56..77e4ac1f 100644
--- a/SHADE_Engine/SHADE_Engine.vcxproj.filters
+++ b/SHADE_Engine/SHADE_Engine.vcxproj.filters
@@ -109,6 +109,9 @@
{B3F7140E-1F0C-3DBF-E88D-E01E546139F0}
+
+ {985A7358-04C5-27CF-4D03-D974B9AC0524}
+
{16CF2D0E-82E3-55BF-4B65-F91EB73852F0}
@@ -327,6 +330,9 @@
Math
+
+ Math
+
Math
@@ -348,12 +354,21 @@
Resource
+
+ Resource
+
Resource
+
+ Resource
+
Resource
+
+ Resource
+
Scene
@@ -361,6 +376,15 @@
Scene
+
+ Scripting
+
+
+ Scripting
+
+
+ Scripting
+
Tools
@@ -373,6 +397,15 @@
Tools
+
+ Tools
+
+
+ Tools
+
+
+ Tools
+
@@ -559,6 +592,12 @@
Scene
+
+ Scripting
+
+
+ Scripting
+
Tools
@@ -568,5 +607,8 @@
Tools
+
+ Tools
+
\ No newline at end of file
diff --git a/SHADE_Engine/src/Engine/ECS_Base/Entity/SHEntity.cpp b/SHADE_Engine/src/Engine/ECS_Base/Entity/SHEntity.cpp
index 6005fb01..edf29ec7 100644
--- a/SHADE_Engine/src/Engine/ECS_Base/Entity/SHEntity.cpp
+++ b/SHADE_Engine/src/Engine/ECS_Base/Entity/SHEntity.cpp
@@ -28,7 +28,7 @@ namespace SHADE
//SHEntityManager::RemoveEntity(this->entityID);
}
- EntityID SHEntity::GetEID() noexcept
+ EntityID SHEntity::GetEID() const noexcept
{
return this->entityID;
}
diff --git a/SHADE_Engine/src/Engine/ECS_Base/Entity/SHEntity.h b/SHADE_Engine/src/Engine/ECS_Base/Entity/SHEntity.h
index d499042c..6f2ae36b 100644
--- a/SHADE_Engine/src/Engine/ECS_Base/Entity/SHEntity.h
+++ b/SHADE_Engine/src/Engine/ECS_Base/Entity/SHEntity.h
@@ -77,7 +77,7 @@ namespace SHADE
* \return uint32_t
* The entityID of this Entity object.
***************************************************************************/
- EntityID GetEID() noexcept;
+ EntityID GetEID() const noexcept;
/*!*************************************************************************
* \brief Set the Active object
diff --git a/SHADE_Engine/src/Scripting/SHDotNetRuntime.cpp b/SHADE_Engine/src/Scripting/SHDotNetRuntime.cpp
new file mode 100644
index 00000000..2d0cec1e
--- /dev/null
+++ b/SHADE_Engine/src/Scripting/SHDotNetRuntime.cpp
@@ -0,0 +1,198 @@
+/*************************************************************************************//*!
+\file SHDotNetRuntime.cpp
+\author Tng Kah Wei, kahwei.tng, 390009620
+\par email: kahwei.tng\@digipen.edu
+\date Oct 2, 2021
+\brief Contains the definition of the SHDotNetRuntime class.
+ Implementation of code to set up code for SHDotNetRuntime is based on the
+ following repository:
+ https://github.com/mjrousos/SampleCoreCLRHost
+
+Copyright (C) 2021 DigiPen Institute of Technology.
+Reproduction or disclosure of this file or its contents without the prior written consent
+of DigiPen Institute of Technology is prohibited.
+*//**************************************************************************************/
+// Precompiled Header
+#include
+// Primary Header
+#include "SHDotNetRuntime.h"
+// Standard Library
+#include
+// External Dependencies
+#include // PathRemoveFileSpecA
+#include "Tools/SHLogger.h"
+
+namespace SHADE
+{
+ /*---------------------------------------------------------------------------------*/
+ /* Constructors/Destructor */
+ /*---------------------------------------------------------------------------------*/
+ SHDotNetRuntime::SHDotNetRuntime(bool autoInit)
+ {
+ if (autoInit)
+ {
+ Init();
+ }
+ }
+
+ SHDotNetRuntime::~SHDotNetRuntime()
+ {
+ if (IsLoaded())
+ {
+ try
+ {
+ Exit();
+ }
+ catch (std::runtime_error& e)
+ {
+ SHLOG_ERROR(e.what());
+ }
+ }
+ }
+
+ /*---------------------------------------------------------------------------------*/
+ /* Lifecycle Functions */
+ /*---------------------------------------------------------------------------------*/
+ void SHDotNetRuntime::Init()
+ {
+ // State checking, in case there was an unload before, we must ensure that the state is valid
+ if (initialised)
+ throw std::runtime_error("[DotNetRuntime] Failed to initialise as it was already initialised or was deinitialised into an invalid state.");
+
+ // Get the current executable directory
+ std::string runtimePath(MAX_PATH, '\0');
+ GetModuleFileNameA(nullptr, runtimePath.data(), MAX_PATH);
+ PathRemoveFileSpecA(runtimePath.data());
+ // Since PathRemoveFileSpecA() removes from data(), the size is not updated, so we must manually update it
+ runtimePath.resize(std::strlen(runtimePath.data()));
+
+ // Do not need to load the library if it was previously loaded
+ if (coreClr == nullptr)
+ {
+ // Construct the CoreCLR path
+ std::string coreClrPath(runtimePath); // Works
+ coreClrPath += "\\coreclr.dll";
+
+ // Load the CoreCLR DLL
+ coreClr = LoadLibraryExA(coreClrPath.c_str(), nullptr, 0);
+ if (!coreClr)
+ {
+ std::ostringstream oss;
+ oss << "[DotNetRuntime] Error #" << GetLastError() << " Failed to load CoreCLR from \"" << coreClrPath << "\"\n";
+ throw std::runtime_error(oss.str());
+ }
+
+ // Step 2: Get CoreCLR hosting functions
+ initializeCoreClr = getCoreClrFunctionPtr("coreclr_initialize");
+ createManagedDelegate = getCoreClrFunctionPtr("coreclr_create_delegate");
+ shutdownCoreClr = getCoreClrFunctionPtr("coreclr_shutdown");
+ }
+
+ // Step 3: Construct AppDomain properties used when starting the runtime
+ // Construct the trusted platform assemblies (TPA) list
+ // This is the list of assemblies that .NET Core can load as
+ // trusted system assemblies (similar to the .NET Framework GAC).
+ // For this host (as with most), assemblies next to CoreCLR will
+ // be included in the TPA list
+ std::string tpaList = buildTpaList(runtimePath);
+
+ // Define CoreCLR properties
+ std::array propertyKeys =
+ {
+ "TRUSTED_PLATFORM_ASSEMBLIES", // Trusted assemblies (like the GAC)
+ "APP_PATHS", // Directories to probe for application assemblies
+ // "APP_NI_PATHS", // Directories to probe for application native images (not used in this sample)
+ // "NATIVE_DLL_SEARCH_DIRECTORIES", // Directories to probe for native dlls (not used in this sample)
+ };
+ std::array propertyValues =
+ {
+ tpaList.c_str(),
+ runtimePath.c_str()
+ };
+
+ // Step 4: Start the CoreCLR runtime
+ int result = initializeCoreClr
+ (
+ runtimePath.c_str(), // AppDomain base path
+ "SHADEHost", // AppDomain friendly name
+ propertyKeys.size(), // Property count
+ propertyKeys.data(), // Property names
+ propertyValues.data(), // Property values
+ &hostHandle, // Host handle
+ &domainId // AppDomain ID
+ );
+
+ // Check if intiialization of CoreCLR failed
+ throwIfFailed("[DotNetRuntime] Failed to initialize CoreCLR.", result);
+
+ initialised = true;
+ SHLOG_INFO("[DotNetRuntime] Successfully loaded the .NET 5.0 Runtime.");
+ }
+
+ void SHDotNetRuntime::Exit()
+ {
+ // State checking, in case there was an unload before, we must ensure that the state is valid
+ if (!initialised)
+ throw std::runtime_error("[DotNetRuntime] Failed to deinitialise as it was not initialised before.");
+
+ // Shutdown CoreCLR
+ int result = shutdownCoreClr(hostHandle, domainId);
+ throwIfFailed("[DotNetRuntime] Failed to shut down CoreCLR.", result);
+
+ // Unset pointers
+ hostHandle = nullptr;
+ domainId = 0;
+ initialised = false;
+
+ SHLOG_INFO("[DotNetRuntime] Successfully shut down the .NET 5.0 Runtime.");
+ }
+
+ /*---------------------------------------------------------------------------------*/
+ /* Helper Functions */
+ /*---------------------------------------------------------------------------------*/
+ std::string SHDotNetRuntime::buildTpaList(const std::string& directory)
+ {
+ // Constants
+ static const std::string SEARCH_PATH = directory + "\\*.dll";
+ static constexpr char PATH_DELIMITER = ';';
+
+ // Create a osstream object to compile the string
+ std::ostringstream tpaList;
+
+ // Search the current directory for the TPAs (.DLLs)
+ WIN32_FIND_DATAA findData;
+ HANDLE fileHandle = FindFirstFileA(SEARCH_PATH.c_str(), &findData);
+ if (fileHandle != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ // Append the assembly to the list
+ tpaList << directory << '\\' << findData.cFileName << PATH_DELIMITER;
+
+ // Note that the CLR does not guarantee which assembly will be loaded if an assembly
+ // is in the TPA list multiple times (perhaps from different paths or perhaps with different NI/NI.dll
+ // extensions. Therefore, a real host should probably add items to the list in priority order and only
+ // add a file if it's not already present on the list.
+ //
+ // For this simple sample, though, and because we're only loading TPA assemblies from a single path,
+ // and have no native images, we can ignore that complication.
+ }
+ while (FindNextFileA(fileHandle, &findData));
+ FindClose(fileHandle);
+ }
+
+ return tpaList.str();
+ }
+
+ void SHDotNetRuntime::throwIfFailed(const std::string& errMsg, int resultCode)
+ {
+ if (resultCode < 0)
+ {
+ std::ostringstream oss;
+ oss << std::hex << std::setfill('0') << std::setw(8)
+ << errMsg
+ << " Error 0x" << resultCode << "\n";
+ throw std::runtime_error(oss.str());
+ }
+ }
+}
diff --git a/SHADE_Engine/src/Scripting/SHDotNetRuntime.h b/SHADE_Engine/src/Scripting/SHDotNetRuntime.h
new file mode 100644
index 00000000..22f8d9c7
--- /dev/null
+++ b/SHADE_Engine/src/Scripting/SHDotNetRuntime.h
@@ -0,0 +1,149 @@
+/*************************************************************************************//*!
+\file SHDotNetRuntime.h
+\author Tng Kah Wei, kahwei.tng, 390009620
+\par email: kahwei.tng\@digipen.edu
+\date Oct 2, 2021
+\brief Contains the interface of a wrapper class for interfacing with the
+ .NET 5 Runtime.
+
+Copyright (C) 2021 DigiPen Institute of Technology.
+Reproduction or disclosure of this file or its contents without the prior written consent
+of DigiPen Institute of Technology is prohibited.
+*//**************************************************************************************/
+#pragma once
+
+// Standard Libraries
+#include // std::setfill, std::setw
+#include // std::runtime_error
+#include // std::string
+#include // std::ostringstream
+// External Dependencies
+#include // HMODULE
+#include // coreclr_*
+
+namespace SHADE
+{
+ /********************************************************************************//*!
+ @brief Class that encapsulates the state of the .NET Core Runtime lifecycle.
+ *//*********************************************************************************/
+ class SHDotNetRuntime
+ {
+ public:
+ /*----------------------------------------------------------------------------------*/
+ /* Constructors/Destructor */
+ /*----------------------------------------------------------------------------------*/
+ /****************************************************************************//*!
+ @brief Default constructor that immediately initializes the CoreCLR.
+
+ @param[in] autoInit
+ If true, loads the CoreCLR by calling Init().
+ *//*****************************************************************************/
+ SHDotNetRuntime(bool autoInit = true);
+ /****************************************************************************//*!
+ @brief Destructor that unloads the CoreCLR if it has not been unloaded
+ yet.
+ *//*****************************************************************************/
+ ~SHDotNetRuntime();
+
+ // Disallow copy and moving
+ SHDotNetRuntime(const SHDotNetRuntime&) = delete;
+ SHDotNetRuntime(SHDotNetRuntime&&) = delete;
+
+ /*----------------------------------------------------------------------------------*/
+ /* Lifecycle Functions */
+ /*----------------------------------------------------------------------------------*/
+ /****************************************************************************//*!
+ @brief Loads the CoreCLR and grabs pointers to bootstrapping functions and
+ kickstarts the CoreCLR.
+
+ @throws SystemExitException
+ Thrown if there is a failure in loading the CLR and related functions.
+ *//*****************************************************************************/
+ void Init();
+ /****************************************************************************//*!
+ @brief Unloads the CoreCLR.
+
+ @throws SystemExitException
+ Thrown if there is a failure in unloading the CLR.
+ *//*****************************************************************************/
+ void Exit();
+
+ /*----------------------------------------------------------------------------------*/
+ /* Usage Functions */
+ /*----------------------------------------------------------------------------------*/
+ /****************************************************************************//*!
+ @brief Checks if the DotNetRuntime has successfully been initialised.
+
+ @return True if this DotNetRuntime has been initialised.
+ *//*****************************************************************************/
+ inline bool IsLoaded() { return coreClr != nullptr; }
+ /****************************************************************************//*!
+ @brief Retrieves a function pointer from the a CLR assembly based on the
+ specified assembly, type and function names.
+
+ @tparam FunctionType
+ Type of the function pointer that the specified function name will
+ provide.
+
+ @params[in] assemblyName
+ Name of the CoreCLR assembly that contains the function.
+ @params[in] typeName
+ Name of the CoreCLR type in the assembly that contains the function.
+ Nested types are separated by a period(.).
+ @params[in] functionName
+ Name of the CoreCLR function to get a pointer to.
+
+ @returns Pointer to the function in the assembly that was specified.
+ *//*****************************************************************************/
+ template
+ FunctionType GetFunctionPtr(const std::string_view& assemblyName,
+ const std::string_view& typeName,
+ const std::string_view& functionName);
+
+ private:
+ /*-----------------------------------------------------------------------------*/
+ /* Data Members */
+ /*-----------------------------------------------------------------------------*/
+ bool initialised = false;
+ // References to CoreCLR key components
+ HMODULE coreClr = nullptr;
+ void* hostHandle = nullptr;
+ unsigned int domainId = 0;
+ // Function Pointers to CoreCLR functions
+ coreclr_initialize_ptr initializeCoreClr = nullptr;
+ coreclr_create_delegate_ptr createManagedDelegate = nullptr;
+ coreclr_shutdown_ptr shutdownCoreClr = nullptr;
+
+ /*-----------------------------------------------------------------------------*/
+ /* Helper Functions */
+ /*-----------------------------------------------------------------------------*/
+ /****************************************************************************//*!
+ @brief Retrieves a function pointer from the CoreCLR based on the specified
+ function name.
+
+ @tparam FunctionType
+ Type of the function pointer that the specified function name will
+ provide.
+
+ @params[in] functionName
+ Name of the CoreCLR function to get a pointer to.
+
+ @returns Pointer to the function in the CoreCLR that was specified.
+ *//*****************************************************************************/
+ template
+ FunctionType getCoreClrFunctionPtr(const std::string& functionName);
+ /****************************************************************************//*!
+ @brief Compiles a semicolon separated string of trusted platform assemblies by
+ searching the specified directory.
+
+ @params[in] directory
+ Path to the directory where the trusted platform assemblies reside.
+
+ @returns Semicolon separated string of trusted platform assemblies.
+ *//*****************************************************************************/
+ static std::string buildTpaList(const std::string& directory);
+ static void throwIfFailed(const std::string& errMsg, int resultCode);
+ };
+} // namespace PlushieEngine::Scripts
+
+#include "SHDotNetRuntime.hpp"
diff --git a/SHADE_Engine/src/Scripting/SHDotNetRuntime.hpp b/SHADE_Engine/src/Scripting/SHDotNetRuntime.hpp
new file mode 100644
index 00000000..3498bc63
--- /dev/null
+++ b/SHADE_Engine/src/Scripting/SHDotNetRuntime.hpp
@@ -0,0 +1,61 @@
+/*************************************************************************************//*!
+\file SHDotNetRuntime.hpp
+\author Tng Kah Wei, kahwei.tng, 390009620
+\par email: kahwei.tng\@digipen.edu
+\date Oct 2, 2021
+\brief Contains the implementation of the template functions of the
+ DotNetRuntime class.
+
+Copyright (C) 2021 DigiPen Institute of Technology.
+Reproduction or disclosure of this file or its contents without the prior written consent
+of DigiPen Institute of Technology is prohibited.
+*//**************************************************************************************/
+#pragma once
+
+// Primary Include
+#include "SHDotNetRuntime.h"
+
+namespace SHADE
+{
+ template
+ FunctionType SHDotNetRuntime::GetFunctionPtr(const std::string_view & assemblyName,
+ const std::string_view & typeName,
+ const std::string_view & functionName)
+ {
+ FunctionType managedDelegate = nullptr;
+ int result = createManagedDelegate
+ (
+ hostHandle,
+ domainId,
+ assemblyName.data(),
+ typeName.data(),
+ functionName.data(),
+ reinterpret_cast(&managedDelegate)
+ );
+
+ // Check if it failed
+ if (result < 0)
+ {
+ std::ostringstream oss;
+ oss << std::hex << std::setfill('0') << std::setw(8)
+ << "[DotNetRuntime] Failed to get pointer to function \""
+ << typeName << "." << functionName << "\" in assembly (" << assemblyName << "). "
+ << "Error 0x" << result << "\n";
+ throw std::runtime_error(oss.str());
+ }
+
+ return managedDelegate;
+ }
+ template
+ FunctionType SHDotNetRuntime::getCoreClrFunctionPtr(const std::string& functionName)
+ {
+ FunctionType fPtr = reinterpret_cast(GetProcAddress(coreClr, functionName.c_str()));
+ if (!fPtr)
+ {
+ std::ostringstream oss;
+ oss << "[DotNetRuntime] Unable to get pointer to function: \"" << functionName << "\"";
+ throw std::runtime_error(oss.str());
+ }
+ return fPtr;
+ }
+}
diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp
new file mode 100644
index 00000000..ac8ad84c
--- /dev/null
+++ b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp
@@ -0,0 +1,509 @@
+/************************************************************************************//*!
+\file SHScriptEngine.cpp
+\author Tng Kah Wei, kahwei.tng, 390009620
+\par email: kahwei.tng\@digipen.edu
+\date Sep 17, 2021
+\brief Contains the implementation for ScriptEngine class.
+
+Copyright (C) 2021 DigiPen Institute of Technology.
+Reproduction or disclosure of this file or its contents without the prior written consent
+of DigiPen Institute of Technology is prohibited.
+*//*************************************************************************************/
+// Precompiled Headers
+#include
+// Primary Header
+#include "SHScriptEngine.h"
+// Standard Library
+#include // std::fstream
+#include // std::filesystem::canonical, std::filesystem::remove
+// Project Headers
+#include "Tools/SHLogger.h"
+#include "Tools/SHStringUtils.h"
+
+namespace SHADE
+{
+ /*--------------------------------------------------------------------------------*/
+ /* Static Definitions */
+ /*--------------------------------------------------------------------------------*/
+ const std::string SHScriptEngine::DEFAULT_CSHARP_NAMESPACE = std::string(DEFAULT_CSHARP_LIB_NAME);
+
+ /*---------------------------------------------------------------------------------*/
+ /* Constructors/Destructors */
+ /*---------------------------------------------------------------------------------*/
+ SHScriptEngine::SHScriptEngine()
+ {}
+
+ /*---------------------------------------------------------------------------------*/
+ /* Lifecycle Functions */
+ /*---------------------------------------------------------------------------------*/
+ void SHScriptEngine::Init()
+ {
+ // Do not allow initialization if already initialised
+ if (dotNet.IsLoaded())
+ {
+ SHLOG_ERROR("[ScriptEngine] Attempted to initialise an already loaded DotNetRuntime.");
+ return;
+ }
+
+ dotNet.Init();
+
+ // Load all the helpers
+ loadFunctions();
+
+ // Generate script assembly if it hasn't been before
+ if (!fileExists(std::string(MANAGED_SCRIPT_LIB_NAME) + ".dll"))
+ {
+ BuildScriptAssembly();
+ }
+
+ // Initialise the CSharp Engine
+ csEngineInit();
+
+ // Link events
+ // - Entity Creation
+ /*onEntityCreate = [this](const SHEntity& e)
+ {
+ csGOLibNotifyNewEntity(e.GetEID());
+ };
+ ECS::OnEntityCreated += onEntityCreate;*/
+ // - Entity Destruction
+ /*onEntityDestroy = [this](const SHEntity& e)
+ {
+ csScriptsRemoveAll(e.GetEID());
+ csGOLibNotifyDestroyEntity(e.GetEID());
+ };
+ ECS::OnEntityDestroy += onEntityDestroy;*/
+ }
+ void SHScriptEngine::UnloadScriptAssembly()
+ {
+ csEngineUnloadScripts();
+ }
+ void SHScriptEngine::LoadScriptAssembly()
+ {
+ csEngineLoadScripts();
+ }
+ void SHScriptEngine::ReloadScriptAssembly()
+ {
+ csEngineReloadScripts();
+ }
+ void SHScriptEngine::ExecuteFixedUpdates()
+ {
+ csScriptsExecuteFixedUpdate();
+ }
+
+ void SHScriptEngine::ExecuteOnTrigger()
+ {
+ csScriptsExecuteOnTrigger();
+ }
+
+ void SHScriptEngine::Exit()
+ {
+ // Do not allow deinitialization if not initialised
+ if (!dotNet.IsLoaded())
+ {
+ SHLOG_ERROR("[ScriptEngine] Attempted to clean up an unloaded DotNetRuntime.");
+ return;
+ }
+
+ // Unlink events
+ /*ECS::OnEntityCreated -= onEntityCreate;
+ ECS::OnEntityDestroy -= onEntityDestroy;*/
+
+ // Clean up the CSharp Engine
+ csEngineExit();
+
+ // Shut down the CLR
+ dotNet.Exit();
+ }
+
+ /*---------------------------------------------------------------------------------*/
+ /* Script Manipulation Functions */
+ /*---------------------------------------------------------------------------------*/
+ bool SHScriptEngine::AddScript(const SHEntity& entity, const std::string_view& scriptName) const
+ {
+ return csScriptsAdd(entity.GetEID(), scriptName.data());
+ }
+ void SHScriptEngine::RemoveAllScripts(const SHEntity& entity) const
+ {
+ csScriptsRemoveAll(entity.GetEID());
+ }
+ void SHScriptEngine::RemoveAllScriptsImmediately(const SHEntity& entity, bool callOnDestroy) const
+ {
+ csScriptsRemoveAllImmediately(entity.GetEID(), callOnDestroy);
+ }
+
+ /*---------------------------------------------------------------------------------*/
+ /* Script Serialisation Functions */
+ /*---------------------------------------------------------------------------------*/
+ std::string SHScriptEngine::SerialiseScripts(const SHEntity& entity) const
+ {
+ // Create buffer needed to store serialised script data
+ constexpr int BUFFER_SIZE = 10240;
+ std::unique_ptr buffer { new char[BUFFER_SIZE] };
+ std::memset(buffer.get(), 0, BUFFER_SIZE);
+
+ // Attempt to serialise the script
+ std::string result;
+ if (csScriptsSerialise(entity.GetEID(), buffer.get(), BUFFER_SIZE))
+ {
+ result = std::string(buffer.get());
+ }
+ else
+ {
+ SHLOG_ERROR("[ScriptEngine] Failed to serialise scripts as string buffer is too small!");
+ }
+
+ // Return an empty string since we failed to serialise
+ return result;
+ }
+
+ /*---------------------------------------------------------------------------------*/
+ /* Script Serialisation Functions */
+ /*---------------------------------------------------------------------------------*/
+ void SHScriptEngine::DeserialiseScript(const SHEntity& entity, const std::string& yaml) const
+ {
+ csScriptDeserialise(entity.GetEID(), yaml.c_str());
+ }
+
+ /*---------------------------------------------------------------------------------*/
+ /* Script Editor Functions */
+ /*---------------------------------------------------------------------------------*/
+ void SHScriptEngine::RenderScriptsInInspector(const SHEntity& entity) const
+ {
+ csEditorRenderScripts(entity.GetEID());
+ }
+
+ /*---------------------------------------------------------------------------------*/
+ /* Static Utility Functions */
+ /*---------------------------------------------------------------------------------*/
+ bool SHScriptEngine::BuildScriptAssembly(bool debug)
+ {
+ constexpr std::string_view BUILD_LOG_PATH = "../Build.log";
+
+ // Prepare directory (delete useless files)
+ deleteFolder("net5.0");
+ deleteFolder("ref");
+ deleteFolder("../PlushieGameManaged");
+ deleteFolder("../obj");
+
+ // Attempt to build the assembly
+ std::ostringstream oss;
+ oss << "[ScriptEngine] Building " << (debug ? " debug " : "") << "Managed Script Assembly (" << MANAGED_SCRIPT_LIB_NAME << ")!";
+ SHLOG_INFO(oss.str());
+ oss.str("");
+ const bool BUILD_SUCCESS = execProcess
+ (
+ L"C:\\Windows\\system32\\cmd.exe",
+ L"/K \"dotnet build \"../PlushieGameManaged.csproj\" -c Debug -o \"./tmp/\" -fl -flp:LogFile=build.log;Verbosity=quiet & exit\""
+ ) == 0;
+ if (BUILD_SUCCESS)
+ {
+ // Copy to built dll to the working directory and replace
+ std::filesystem::copy_file("./tmp/PlushieGameManaged.dll", "PlushieGameManaged.dll", std::filesystem::copy_options::overwrite_existing);
+
+ oss << "[ScriptEngine] Successfully built Managed Script Assembly (" << MANAGED_SCRIPT_LIB_NAME << ")!";
+ SHLOG_INFO(oss.str());
+ }
+ else
+ {
+ oss << "[ScriptEngine] Failed to build Managed Script Assembly (" << MANAGED_SCRIPT_LIB_NAME << ")!";
+ SHLOG_ERROR(oss.str());
+ }
+
+ // Clean up built files
+ deleteFolder("./tmp");
+
+ // Read the build log and output to the console
+ dumpBuildLog(BUILD_LOG_PATH);
+ // Delete the build log file since we no longer need it
+ deleteFile(BUILD_LOG_PATH);
+
+ return BUILD_SUCCESS;
+ }
+
+ void SHScriptEngine::GenerateScriptsCsProjFile(const std::filesystem::path& path)
+ {
+ // Sample
+ static std::string_view FILE_CONTENTS =
+"\n\
+ \n\
+ net5.0\n\
+ x64\n\
+ Release;Debug\n\
+ \n\
+ \n\
+ .\\bin_Release-x64\n\
+ x64\n\
+ \n\
+ \n\
+ .\\bin_Debug-x64\n\
+ x64\n\
+ DEBUG;TRACE\n\
+ false\n\
+ full\n\
+ true\n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ .\\bin\\PlushieAPI.dll\n\
+ \n\
+ \n\
+";
+
+ // Attempt to create the file
+ std::ofstream file(path);
+ if (!file.is_open())
+ throw std::runtime_error("Unable to create CsProj file!");
+
+ // Fill the file
+ file << FILE_CONTENTS;
+
+ // Close
+ file.close();
+ }
+
+ /*---------------------------------------------------------------------------------*/
+ /* Helper Functions */
+ /*---------------------------------------------------------------------------------*/
+ void SHScriptEngine::loadFunctions()
+ {
+ std::ostringstream oss;
+ oss << "[ScriptEngine] Loading \"" << DEFAULT_CSHARP_LIB_NAME << "\" CLR library.";
+ SHLOG_INFO(oss.str());
+
+ // Load functions
+ csEngineInit = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".EngineInterface",
+ "Init"
+ );
+ csEngineLoadScripts = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".EngineInterface",
+ "LoadScriptAssembly"
+ );
+ csEngineUnloadScripts = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".EngineInterface",
+ "UnloadScriptAssembly"
+ );
+ csEngineReloadScripts = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".EngineInterface",
+ "ReloadScriptAssembly"
+ );
+ csEngineExit = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".EngineInterface",
+ "Exit"
+ );
+ csScriptsFrameSetUp = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
+ "FrameSetUp"
+ );
+ csScriptsExecuteOnTrigger = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
+ "ExecuteOnTrigger"
+ );
+ csScriptsExecuteFixedUpdate = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
+ "ExecuteFixedUpdate"
+ );
+ csScriptsExecuteUpdate = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
+ "ExecuteUpdate"
+ );
+ csScriptsExecuteLateUpdate = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
+ "ExecuteLateUpdate"
+ );
+ csScriptsFrameCleanUp = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
+ "FrameCleanUp"
+ );
+ csScriptsAdd = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
+ "AddScriptViaName"
+ );
+ csScriptsRemoveAll = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
+ "RemoveAllScripts"
+ );
+ csScriptsRemoveAllImmediately = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
+ "RemoveAllScriptsImmediately"
+ );
+ csScriptsSerialise = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
+ "SerialiseScripts"
+ );
+ csScriptsSerialiseJson = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
+ "SerialiseScriptsJson"
+ );
+ csScriptDeserialise = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
+ "DeserialiseScript"
+ );
+ csGOLibNotifyNewEntity = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".GameObjectLibrary",
+ "NotifyNewGameObject"
+ );
+ csGOLibNotifyDestroyEntity = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".GameObjectLibrary",
+ "NotifyDestroyGameObject"
+ );
+ csEditorRenderScripts = dotNet.GetFunctionPtr
+ (
+ DEFAULT_CSHARP_LIB_NAME,
+ DEFAULT_CSHARP_NAMESPACE + ".Editor",
+ "RenderScriptsInInspector"
+ );
+ }
+
+ void SHScriptEngine::dumpBuildLog(const std::string_view& buildLogPath)
+ {
+ std::ifstream buildLog(buildLogPath);
+
+ // Fail to open
+ if (!buildLog.is_open())
+ return;
+
+ // Process line by line
+ std::string line;
+ while (std::getline(buildLog, line))
+ {
+ if (line.find("error") != line.npos)
+ {
+ SHLOG_ERROR(line);
+ }
+ else
+ {
+ SHLOG_WARNING(line);
+ }
+ }
+ }
+ void SHScriptEngine::deleteFile(const std::string_view& filePath)
+ {
+ try
+ {
+ std::filesystem::remove(std::filesystem::canonical(filePath));
+ }
+ catch (...) {} // Ignore deletion failures
+ }
+
+ void SHScriptEngine::deleteFolder(const std::string_view& filePath)
+ {
+ try
+ {
+ std::filesystem::remove_all(std::filesystem::canonical(filePath));
+ }
+ catch (...) {} // Ignore deletion failures
+ }
+
+ bool SHScriptEngine::fileExists(const std::string_view& filePath)
+ {
+ std::error_code error;
+ if (std::filesystem::exists(filePath, error))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ DWORD SHScriptEngine::execProcess(const std::wstring& path, const std::wstring& args)
+ {
+ STARTUPINFOW startInfo;
+ PROCESS_INFORMATION procInfo;
+ ZeroMemory(&startInfo, sizeof(startInfo));
+ ZeroMemory(&procInfo, sizeof(procInfo));
+ startInfo.cb = sizeof(startInfo);
+
+ std::wstring argsWstr = args;
+
+ // Start Process
+ const auto SUCCESS = CreateProcess
+ (
+ path.data(), argsWstr.data(),
+ nullptr, nullptr, false, NULL, nullptr, nullptr,
+ &startInfo, &procInfo
+ );
+
+ // Error Check
+ if (!SUCCESS)
+ {
+ auto err = GetLastError();
+ std::ostringstream oss;
+ oss << "[ScriptEngine] Failed to launch process. Error code: " << std::hex << err
+ << " (" << SHStringUtils::GetWin32ErrorMessage(err) << ")";
+ throw std::runtime_error(oss.str());
+ }
+
+ // Wait for execution to end
+ DWORD status;
+ while (true)
+ {
+ const auto SUCCESS = GetExitCodeProcess(procInfo.hProcess, &status);
+ if (!SUCCESS)
+ {
+ auto err = GetLastError();
+ std::ostringstream oss;
+ oss << "[ScriptEngine] Failed to query process. Error code: " << std::hex << err
+ << " (" << SHStringUtils::GetWin32ErrorMessage(err) << ")";
+ throw std::runtime_error(oss.str());
+ }
+
+ // Break only if process ends
+ if (status != STILL_ACTIVE)
+ {
+ CloseHandle(procInfo.hProcess);
+ CloseHandle(procInfo.hThread);
+ return status;
+ }
+ }
+ }
+
+}
diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.h b/SHADE_Engine/src/Scripting/SHScriptEngine.h
new file mode 100644
index 00000000..85e3ac3f
--- /dev/null
+++ b/SHADE_Engine/src/Scripting/SHScriptEngine.h
@@ -0,0 +1,258 @@
+/************************************************************************************//*!
+\file ScriptEngine.h
+\author Tng Kah Wei, kahwei.tng, 390009620
+\par email: kahwei.tng\@digipen.edu
+\date Sep 17, 2021
+\brief Contains the interface for ScriptEngine class.
+
+Copyright (C) 2021 DigiPen Institute of Technology.
+Reproduction or disclosure of this file or its contents without the prior written consent
+of DigiPen Institute of Technology is prohibited.
+*//*************************************************************************************/
+#pragma once
+
+// STL Includes
+#include
+
+// Project Headers
+#include "SHDotNetRuntime.h"
+#include "Engine/ECS_Base/SHECSMacros.h"
+#include "Engine/ECS_Base/Entity/SHEntity.h"
+
+namespace SHADE
+{
+ ///
+ /// Manages initialisation of the DotNetRuntime and interfacing with CLR code written
+ /// and executed on .NET.
+ ///
+ class SHScriptEngine
+ {
+ public:
+ /*-----------------------------------------------------------------------------*/
+ /* Constructors & Destructors */
+ /*-----------------------------------------------------------------------------*/
+ ///
+ /// Default Constructor
+ ///
+ SHScriptEngine();
+
+ /*-----------------------------------------------------------------------------*/
+ /* Lifecycle Functions */
+ /*-----------------------------------------------------------------------------*/
+ ///
+ /// Initialises the DotNetRuntime and retrieves function pointers to all
+ /// functions on the CLR used to interface with the engine.
+ ///
+ void Init();
+ ///
+ /// Loads the managed script assembly. Ensure this is only called after
+ /// UnloadScriptAssembly() has been called.
+ ///
+ void UnloadScriptAssembly();
+ ///
+ /// Unloads the managed script assembly.
+ /// Take note that this will clear all existing scripts, ensure that the scene
+ /// is saved before doing so.
+ ///
+ void LoadScriptAssembly();
+ ///
+ /// Reloads the managed script assembly.
+ /// Take note that this will clear all existing scripts, ensure that the scene
+ /// is saved before doing so.
+ ///
+ void ReloadScriptAssembly();
+ ///
+ /// Executes the FixedUpdate()s of the PlushieScripts that are attached to
+ /// Entities.
+ ///
+ void ExecuteFixedUpdates();
+ ///
+ /// Executes the OnTrigger() family of functions of the PlushieScripts that are
+ /// attached to Entities.
+ ///
+ void ExecuteOnTrigger();
+ ///
+ /// Shuts down the DotNetRuntime.
+ ///
+ void Exit();
+
+ /*-----------------------------------------------------------------------------*/
+ /* Script Manipulation Functions */
+ /*-----------------------------------------------------------------------------*/
+ ///
+ /// Adds a Script to a specified Entity. Note that while you can call this
+ /// multiple times on a specified Entity, it will work for all intents and
+ /// purposes but GetScript<T>() (C# only) currently only
+ /// gives you the first PlushieScript added of the specified type.
+ ///
+ /// The entity to add a script to.
+ /// Type name of the script to add.
+ ///
+ /// True if successfully added. False otherwise with the error logged to the
+ /// console.
+ ///
+ bool AddScript(const SHEntity& entity, const std::string_view& scriptName) const;
+ ///
+ /// Removes all Scripts attached to the specified Entity. Does not do anything
+ /// if the specified Entity is invalid or does not have any PlushieScripts
+ /// attached.
+ ///
+ /// The entity to remove the scripts from.
+ void RemoveAllScripts(const SHEntity& entity) const;
+ ///
+ /// Removes all Scripts attached to the specified Entity. Unlike
+ /// RemoveAllScripts(), this removes all the scripts immediately.
+ /// Does not do anything if the specified Entity is invalid or does not have any
+ /// PlushieScripts attached.
+ ///
+ /// The entity to remove the scripts from.
+ ///
+ /// Whether or not to call OnDestroy on the scripts. This is ignored if not in
+ /// play mode.
+ ///
+ void RemoveAllScriptsImmediately(const SHEntity& entity, bool callOnDestroy) const;
+
+ /*-----------------------------------------------------------------------------*/
+ /* Script Serialisation Functions */
+ /*-----------------------------------------------------------------------------*/
+ ///
+ /// Generates a JSON string that represents the set of Scripts attached to the
+ /// specified Entity.
+ ///
+ /// The Entity to Serialise.
+ ///
+ /// String that represents the set of scripts attached to the specified Entity.
+ ///
+ std::string SerialiseScripts(const SHEntity& entity) const;
+ ///
+ /// Loads the specified JSON string and creates a Script for the specified Entity
+ /// based on the specified JSON string.
+ ///
+ /// The Entity to deserialise a Script on to.
+ ///
+ /// The JSON string that represents the Script to load into the Entity.
+ ///
+ void DeserialiseScript(const SHEntity& entity, const std::string& yaml) const;
+
+ /*-----------------------------------------------------------------------------*/
+ /* Script Editor Functions */
+ /*-----------------------------------------------------------------------------*/
+ ///
+ /// Renders the set of attached PlushieScripts for the specified Entity into the
+ /// inspector.
+ ///
+ /// This function is meant for consumption from native code in the inspector
+ /// rendering code.
+ ///
+ /// The Entity to render the PlushieScripts of.
+ void RenderScriptsInInspector(const SHEntity& entity) const;
+
+ /*-----------------------------------------------------------------------------*/
+ /* Static Utility Functions */
+ /*-----------------------------------------------------------------------------*/
+ ///
+ /// Utilises execution of a external batch file for invoking the dotnet build
+ /// tool to compile C# scripts in the Assets folder into the PlushieGameManaged
+ /// C# assembly DLL.
+ ///
+ ///
+ /// Whether or not a debug build will be built. Only debug built C# assemblies
+ /// can be debugged.
+ ///
+ /// Whether or not the build succeeded.
+ static bool BuildScriptAssembly(bool debug = false);
+ ///
+ /// Generates a .csproj file for editing and compiling the C# scripts.
+ ///
+ /// File path to the generated file.
+ static void GenerateScriptsCsProjFile(const std::filesystem::path& path);
+
+ private:
+ /*-----------------------------------------------------------------------------*/
+ /* Type Definitions */
+ /*-----------------------------------------------------------------------------*/
+ using CsFuncPtr = void(*)(void);
+ using CsScriptManipFuncPtr = bool(*)(EntityID, const char*);
+ using CsScriptBasicFuncPtr = void(*)(EntityID);
+ using CsScriptOptionalFuncPtr = void(*)(EntityID, bool);
+ using CsScriptSerialiseFuncPtr = bool(*)(EntityID, char*, int);
+ using CsScriptDeserialiseFuncPtr = bool(*)(EntityID, const char*);
+ using CsScriptSerialiseJsonFuncPtr = bool(*)(EntityID, void*);
+ using CsScriptEditorFuncPtr = void(*)(EntityID);
+
+ /*-----------------------------------------------------------------------------*/
+ /* Constants */
+ /*-----------------------------------------------------------------------------*/
+ static constexpr std::string_view DEFAULT_CSHARP_LIB_NAME = "SHADEAPI";
+ static constexpr std::string_view MANAGED_SCRIPT_LIB_NAME = "SHADEManaged";
+ static const std::string DEFAULT_CSHARP_NAMESPACE;
+
+ /*-----------------------------------------------------------------------------*/
+ /* Data Members */
+ /*-----------------------------------------------------------------------------*/
+ SHDotNetRuntime dotNet {false};
+ // Function Pointers to CLR Code
+ // - Engine Init
+ CsFuncPtr csEngineInit = nullptr;
+ CsFuncPtr csEngineLoadScripts = nullptr;
+ CsFuncPtr csEngineUnloadScripts = nullptr;
+ CsFuncPtr csEngineReloadScripts = nullptr;
+ CsFuncPtr csEngineExit = nullptr;
+ // - Scripts Store
+ CsFuncPtr csScriptsFrameSetUp = nullptr;
+ CsFuncPtr csScriptsExecuteOnTrigger = nullptr;
+ CsFuncPtr csScriptsExecuteFixedUpdate = nullptr;
+ CsFuncPtr csScriptsExecuteUpdate = nullptr;
+ CsFuncPtr csScriptsExecuteLateUpdate = nullptr;
+ CsFuncPtr csScriptsFrameCleanUp = nullptr;
+ CsScriptManipFuncPtr csScriptsAdd = nullptr;
+ CsScriptBasicFuncPtr csScriptsRemoveAll = nullptr;
+ CsScriptOptionalFuncPtr csScriptsRemoveAllImmediately = nullptr;
+ CsScriptSerialiseFuncPtr csScriptsSerialise = nullptr;
+ CsScriptDeserialiseFuncPtr csScriptDeserialise = nullptr;
+ CsScriptSerialiseJsonFuncPtr csScriptsSerialiseJson = nullptr;
+ CsScriptSerialiseJsonFuncPtr csScriptDeserialiseJson = nullptr;
+ // - GameObject Library
+ CsScriptBasicFuncPtr csGOLibNotifyNewEntity = nullptr;
+ CsScriptBasicFuncPtr csGOLibNotifyDestroyEntity = nullptr;
+ // - Editor
+ CsScriptEditorFuncPtr csEditorRenderScripts = nullptr;
+ // Delegates
+ /*ECS::EntityEvent::Delegate onEntityCreate;
+ ECS::EntityEvent::Delegate onEntityDestroy;*/
+
+ /*-----------------------------------------------------------------------------*/
+ /* Helper Functions */
+ /*-----------------------------------------------------------------------------*/
+ ///
+ /// Loads all the function pointers to CLR code that we need to execute.
+ ///
+ void loadFunctions();
+ ///
+ /// Reads the file via the specified path that represents a build log of error
+ /// and warning messages.
+ ///
+ ///
+ /// File path to the build log of script builds done by BuildScriptAssembly() to
+ /// dump and process.
+ ///
+ static void dumpBuildLog(const std::string_view& buildLogPath);
+ ///
+ /// Deletes the file as specified by the file path.
+ ///
+ /// File path to the file to delete.
+ static void deleteFile(const std::string_view& filePath);
+ ///
+ /// Deletes the folder and all files in it as specified by the file path.
+ ///
+ /// File path to the file to delete.
+ static void deleteFolder(const std::string_view& filePath);
+ ///
+ /// Checks if a specified file exists.
+ ///
+ /// File path to the file to check.
+ /// True if the file exists
+ static bool fileExists(const std::string_view& filePath);
+ static DWORD execProcess(const std::wstring& path, const std::wstring& args);
+ };
+} // namespace PlushieEngine
diff --git a/SHADE_Engine/src/Tools/SHStringUtils.cpp b/SHADE_Engine/src/Tools/SHStringUtils.cpp
new file mode 100644
index 00000000..a8dc4a0c
--- /dev/null
+++ b/SHADE_Engine/src/Tools/SHStringUtils.cpp
@@ -0,0 +1,52 @@
+/************************************************************************************//*!
+\file StringUtilities.cpp
+\author Tng Kah Wei, kahwei.tng, 390009620
+\par email: kahwei.tng\@digipen.edu
+\date Nov 29, 2021
+\brief Contains the definition of functions for working with strings.
+
+Copyright (C) 2021 DigiPen Institute of Technology.
+Reproduction or disclosure of this file or its contents without the prior written consent
+of DigiPen Institute of Technology is prohibited.
+*//*************************************************************************************/
+// Precompiled Header
+#include
+// Primary Header
+#include "SHStringUtils.h"
+
+namespace SHADE
+{
+ /*---------------------------------------------------------------------------------*/
+ /* Utility Functions */
+ /*---------------------------------------------------------------------------------*/
+ std::vector SHStringUtils::Split(const std::string& str, const char& delim)
+ {
+ return Split(str, delim);
+ }
+ std::vector SHStringUtils::Split(const std::wstring& str, const wchar_t& delim)
+ {
+ return Split(str, delim);
+ }
+ std::string SHStringUtils::WstrToStr(const std::wstring& wstr)
+ {
+ static std::vector buffer;
+ const int STR_SIZE = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast(wstr.size()), nullptr, 0, nullptr, nullptr) + 1 /* Null Terminator */;
+ buffer.resize(STR_SIZE);
+ WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast(wstr.size()), buffer.data(), MAX_PATH, nullptr, nullptr);
+ return std::string(buffer.data());
+ }
+ std::wstring SHStringUtils::StrToWstr(const std::string& str)
+ {
+ static std::vector buffer;
+ const int WSTR_SIZE = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.size()), nullptr, 0) + 1 /* Null Terminator */;
+ buffer.resize(WSTR_SIZE);
+ MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.size()), buffer.data(), WSTR_SIZE);
+ return std::wstring(buffer.data());
+ }
+
+ std::string SHStringUtils::GetWin32ErrorMessage(unsigned long errorCode)
+ {
+ return std::system_category().message(errorCode);
+ }
+
+} // namespace PlushieEngine
\ No newline at end of file
diff --git a/SHADE_Engine/src/Tools/SHStringUtils.h b/SHADE_Engine/src/Tools/SHStringUtils.h
new file mode 100644
index 00000000..abfe9146
--- /dev/null
+++ b/SHADE_Engine/src/Tools/SHStringUtils.h
@@ -0,0 +1,81 @@
+/************************************************************************************//*!
+\file StringUtilities.h
+\author Tng Kah Wei, kahwei.tng, 390009620
+\par email: kahwei.tng\@digipen.edu
+\date Nov 29, 2021
+\brief Contains the declaration of functions for working with files and folders.
+
+Copyright (C) 2021 DigiPen Institute of Technology.
+Reproduction or disclosure of this file or its contents without the prior written consent
+of DigiPen Institute of Technology is prohibited.
+*//*************************************************************************************/
+#pragma once
+// Standard Libraries
+#include // std::basic_string
+#include // std::vector
+
+namespace SHADE
+{
+ ///
+ /// Contains useful functions for operating on strings.
+ ///
+ class SHStringUtils
+ {
+ public:
+ /*-----------------------------------------------------------------------------*/
+ /* Utility Functions */
+ /*-----------------------------------------------------------------------------*/
+
+ ///
+ /// Splits a string separated by a specified delimiter into a vector of strings.
+ ///
+ /// Internal type of each element in the string.
+ /// Read only reference to the string to split.
+ /// Read only reference to the delimiter.
+ /// Vector of strings that have been split.
+ template
+ static std::vector> Split(const std::basic_string& str, const T& delim);
+ ///
+ /// Splits a string separated by a specified delimiter into a vector of strings.
+ /// Overload of Split() to allow for string literals to be accepted.
+ ///
+ /// Read only reference to the string to split.
+ /// Read only reference to the delimiter.
+ /// Vector of strings that have been split.
+ static std::vector Split(const std::string& str, const char& delim);
+ ///
+ /// Splits a string separated by a specified delimiter into a vector of strings.
+ /// Overload of Split() to allow for wide string literals to be accepted.
+ ///
+ /// Read only reference to the string to split.
+ /// Read only reference to the delimiter.
+ /// Vector of strings that have been split.
+ static std::vector Split(const std::wstring& str, const wchar_t& delim);
+ ///
+ /// Converts a wstring to a string.
+ ///
+ /// wstring to convert.
+ /// The converted wstring in string form.
+ static std::string WstrToStr(const std::wstring& wstr);
+ ///
+ /// Converts a string to a wstring.
+ ///
+ /// string to convert.
+ /// The converted string in wstring form.
+ static std::wstring StrToWstr(const std::string& str);
+ ///
+ /// Retrieves the error message associated with a Win32 error code.
+ ///
+ /// Win32 error code to decode.
+ /// String that represents the Win32 error.
+ static std::string GetWin32ErrorMessage(unsigned long errorCode);
+
+ private:
+ /*-------------------------------------------------------------------------------*/
+ /* Constructors/Destructors */
+ /*-------------------------------------------------------------------------------*/
+ SHStringUtils() = delete;
+ };
+} // namespace PlushieEngine
+
+#include "SHStringUtils.hpp"
diff --git a/SHADE_Engine/src/Tools/SHStringUtils.hpp b/SHADE_Engine/src/Tools/SHStringUtils.hpp
new file mode 100644
index 00000000..5b4caecb
--- /dev/null
+++ b/SHADE_Engine/src/Tools/SHStringUtils.hpp
@@ -0,0 +1,46 @@
+/************************************************************************************//*!
+\file StringUtilities.hpp
+\author Tng Kah Wei, kahwei.tng, 390009620
+\par email: kahwei.tng\@digipen.edu
+\date Nov 29, 2021
+\brief Contains the implementation of template functions for working with files
+ and folders.
+
+Copyright (C) 2021 DigiPen Institute of Technology.
+Reproduction or disclosure of this file or its contents without the prior written consent
+of DigiPen Institute of Technology is prohibited.
+*//*************************************************************************************/
+#pragma once
+// Primary Header
+#include "SHStringUtils.h"
+
+namespace SHADE
+{
+ /*-------------------------------------------------------------------------------*/
+ /* Template Function Definitions */
+ /*-------------------------------------------------------------------------------*/
+ template
+ inline std::vector> SHStringUtils::Split(const std::basic_string& str, const T& delim)
+ {
+ std::vector> results;
+ std::basic_string remaining = str;
+
+ // Go through looking for delimiters
+ while (true)
+ {
+ const size_t DELIM_POS = remaining.find_first_of(delim);
+ results.emplace_back(remaining.substr(0, DELIM_POS));
+
+ // Check if we hit the end of the string
+ if (DELIM_POS == remaining.npos)
+ {
+ break;
+ }
+
+ // Otherwise, cut the remainder
+ remaining = remaining.substr(DELIM_POS + 1);
+ }
+
+ return results;
+ }
+} // namespace PlushieEngine
\ No newline at end of file