aboutsummaryrefslogtreecommitdiff
path: root/src/libjin/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/libjin/common')
-rw-r--r--src/libjin/common/je_array.hpp123
-rw-r--r--src/libjin/common/je_common.h7
-rw-r--r--src/libjin/common/je_exception.cpp46
-rw-r--r--src/libjin/common/je_exception.h45
-rw-r--r--src/libjin/common/je_noncopyable.h24
-rw-r--r--src/libjin/common/je_object.h16
-rw-r--r--src/libjin/common/je_pool.hpp177
-rw-r--r--src/libjin/common/je_singleton.hpp80
-rw-r--r--src/libjin/common/je_stringmap.hpp143
-rw-r--r--src/libjin/common/je_subsystem.hpp78
-rw-r--r--src/libjin/common/je_temporary.h31
-rw-r--r--src/libjin/common/je_types.h34
-rw-r--r--src/libjin/common/je_utf8.cpp42
-rw-r--r--src/libjin/common/je_utf8.h34
14 files changed, 880 insertions, 0 deletions
diff --git a/src/libjin/common/je_array.hpp b/src/libjin/common/je_array.hpp
new file mode 100644
index 0000000..8a5cbf1
--- /dev/null
+++ b/src/libjin/common/je_array.hpp
@@ -0,0 +1,123 @@
+#ifndef __JE_COMMON_ARRAY_H__
+#define __JE_COMMON_ARRAY_H__
+
+namespace JinEngine
+{
+
+ ///
+ /// A array created on heap.
+ ///
+ template<typename T>
+ class Array
+ {
+ public:
+ ///
+ /// Array constructor.
+ ///
+ Array()
+ : length(0)
+ , data(nullptr)
+ {
+ }
+
+ ///
+ /// Array constructor.
+ ///
+ /// @param l Length of array.
+ ///
+ Array(int l)
+ {
+ length = l;
+ data = new T[l];
+ }
+
+ ///
+ /// Array destructor.
+ ///
+ ~Array()
+ {
+ delete[] data;
+ length = 0;
+ }
+
+ ///
+ /// Get address of data.
+ ///
+ /// @return Address of data.
+ ///
+ T* operator &()
+ {
+ return data;
+ }
+
+ ///
+ /// Get specific element of array.
+ ///
+ /// @return Element of array.
+ ///
+ T& operator[](int index)
+ {
+ return data[index];
+ }
+
+ ///
+ /// Bind data with given data.
+ ///
+ /// @param data Data pointer.
+ /// @param length Length of data.
+ ///
+ void bind(T* data, int length)
+ {
+ if (data != nullptr)
+ delete data;
+ this->data = data;
+ this->length = length;
+ }
+
+ ///
+ /// Add an element.
+ ///
+ /// @param v Value of element.
+ ///
+ void add(T value)
+ {
+ int len = length + 1;
+ T* d = new T[len];
+ memcpy(d, data, size());
+ d[length] = value;
+ bind(d, len);
+ }
+
+ ///
+ /// Get size of data in byte.
+ ///
+ /// @return Size of data in byte.
+ ///
+ int size()
+ {
+ return sizeof(T) * length;
+ }
+
+ ///
+ /// Get length of data.
+ ///
+ /// @return Count of data.
+ ///
+ int count()
+ {
+ return length;
+ }
+
+ private:
+ // Disable new and delete.
+ void* operator new(size_t t);
+ void operator delete(void* ptr);
+
+ T * data;
+ unsigned int length;
+
+ };
+
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_common.h b/src/libjin/common/je_common.h
new file mode 100644
index 0000000..31b67c6
--- /dev/null
+++ b/src/libjin/common/je_common.h
@@ -0,0 +1,7 @@
+#ifndef __JE_COMMON_H__
+#define __JE_COMMON_H__
+
+#include "je_exception.h"
+#include "je_array.hpp"
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_exception.cpp b/src/libjin/common/je_exception.cpp
new file mode 100644
index 0000000..5489a42
--- /dev/null
+++ b/src/libjin/common/je_exception.cpp
@@ -0,0 +1,46 @@
+#include <stdarg.h>
+
+#include "je_exception.h"
+
+namespace JinEngine
+{
+
+ Exception::Exception(const char *fmt, ...)
+ {
+ va_list args;
+ int size_buffer = 256, size_out;
+ char *buffer;
+ while (true)
+ {
+ buffer = new char[size_buffer];
+ memset(buffer, 0, size_buffer);
+
+ va_start(args, fmt);
+ size_out = vsnprintf(buffer, size_buffer, fmt, args);
+ va_end(args);
+
+ // see http://perfec.to/vsnprintf/pasprintf.c
+ // if size_out ...
+ // == -1 --> output was truncated
+ // == size_buffer --> output was truncated
+ // == size_buffer-1 --> ambiguous, /may/ have been truncated
+ // > size_buffer --> output was truncated, and size_out
+ // bytes would have been written
+ if (size_out == size_buffer || size_out == -1 || size_out == size_buffer - 1)
+ size_buffer *= 2;
+ else if (size_out > size_buffer)
+ size_buffer = size_out + 2; // to avoid the ambiguous case
+ else
+ break;
+
+ delete[] buffer;
+ }
+ mMessage = std::string(buffer);
+ delete[] buffer;
+ }
+
+ Exception::~Exception() throw()
+ {
+ }
+
+} // namespace JinEngine \ No newline at end of file
diff --git a/src/libjin/common/je_exception.h b/src/libjin/common/je_exception.h
new file mode 100644
index 0000000..c319ebd
--- /dev/null
+++ b/src/libjin/common/je_exception.h
@@ -0,0 +1,45 @@
+#ifndef __JE_EXCEPTION_H__
+#define __JE_EXCEPTION_H__
+
+#include <exception>
+#include <string>
+
+namespace JinEngine
+{
+
+ ///
+ /// Jin Exception.
+ ///
+ class Exception : public std::exception
+ {
+ public:
+
+ ///
+ /// Creates a new Exception according to printf-rules.
+ ///
+ /// @param fmt The format string (see printf).
+ ///
+ Exception(const char *fmt, ...);
+ virtual ~Exception() throw();
+
+ ///
+ /// Returns a string containing reason for the exception.
+ ///
+ /// @return A description of the exception.
+ ///
+ inline virtual const char *what() const throw()
+ {
+ return mMessage.c_str();
+ }
+
+ private:
+ ///
+ /// Exception message.
+ ///
+ std::string mMessage;
+
+ };
+
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_noncopyable.h b/src/libjin/common/je_noncopyable.h
new file mode 100644
index 0000000..eff7121
--- /dev/null
+++ b/src/libjin/common/je_noncopyable.h
@@ -0,0 +1,24 @@
+#ifndef __JE_NONCOPYABLE_H__
+#define __JE_NONCOPYABLE_H__
+
+namespace JinEngine
+{
+
+ ///
+ /// Class inherites this could not be copied.
+ ///
+ class Noncopyable
+ {
+ public:
+ Noncopyable(void) { }
+ virtual ~Noncopyable(void) { }
+
+ private:
+ Noncopyable(const Noncopyable& other);
+ Noncopyable& operator=(const Noncopyable& other);
+
+ };
+
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_object.h b/src/libjin/common/je_object.h
new file mode 100644
index 0000000..fb8221f
--- /dev/null
+++ b/src/libjin/common/je_object.h
@@ -0,0 +1,16 @@
+#ifndef __JE_OBJECT_H__
+#define __JE_OBJECT_H__
+
+namespace JinEngine
+{
+
+ ///
+ /// Base class of all objects in Jin.
+ ///
+ class Object
+ {
+ };
+
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_pool.hpp b/src/libjin/common/je_pool.hpp
new file mode 100644
index 0000000..cb96c5b
--- /dev/null
+++ b/src/libjin/common/je_pool.hpp
@@ -0,0 +1,177 @@
+///
+/// An O(1) Object Pool. Original from https://www.codeproject.com/Articles/746630/O-Object-Pool-in-Cplusplus
+///
+#ifndef __JE_POOL_H__
+#define __JE_POOL_H__
+
+#include <stdlib.h>
+#include <iostream>
+
+#include "je_types.h"
+
+namespace JinEngine
+{
+
+ class DefaultMemoryAllocator
+ {
+ public:
+ static inline void *Allocate(size_t size)
+ {
+ return ::operator new(size, ::std::nothrow);
+ }
+ static inline void Deallocate(void *pointer, size_t size)
+ {
+ ::operator delete(pointer);
+ }
+ };
+
+ template<typename T, class TMemoryAllocator = DefaultMemoryAllocator>
+ class Pool
+ {
+ private:
+ struct _Node
+ {
+ void *_memory;
+ size_t _capacity;
+ _Node *_nextNode;
+
+ _Node(size_t capacity)
+ {
+ if (capacity < 1)
+ throw std::invalid_argument("capacity must be at least 1.");
+
+ _memory = TMemoryAllocator::Allocate(_itemSize * capacity);
+ if (_memory == NULL)
+ throw std::bad_alloc();
+
+ _capacity = capacity;
+ _nextNode = NULL;
+ }
+ ~_Node()
+ {
+ TMemoryAllocator::Deallocate(_memory, _itemSize * _capacity);
+ }
+ };
+
+ void *_nodeMemory;
+ T *_firstDeleted;
+ size_t _countInNode;
+ size_t _nodeCapacity;
+ _Node _firstNode;
+ _Node *_lastNode;
+ size_t _maxBlockLength;
+
+ static const size_t _itemSize;
+
+ Pool(const Pool<T, TMemoryAllocator> &source);
+ void operator = (const Pool<T, TMemoryAllocator> &source);
+
+ void _AllocateNewNode()
+ {
+ size_t size = _countInNode;
+ if (size >= _maxBlockLength)
+ size = _maxBlockLength;
+ else
+ {
+ size *= 2;
+
+ if (size < _countInNode)
+ throw std::overflow_error("size became too big.");
+
+ if (size >= _maxBlockLength)
+ size = _maxBlockLength;
+ }
+
+ _Node *newNode = new _Node(size);
+ _lastNode->_nextNode = newNode;
+ _lastNode = newNode;
+ _nodeMemory = newNode->_memory;
+ _countInNode = 0;
+ _nodeCapacity = size;
+ }
+
+ public:
+ explicit Pool(size_t initialCapacity = 32, size_t maxBlockLength = 1000000)
+ : _firstDeleted(NULL)
+ , _countInNode(0)
+ , _nodeCapacity(initialCapacity)
+ , _firstNode(initialCapacity)
+ , _maxBlockLength(maxBlockLength)
+ {
+ if (maxBlockLength < 1)
+ throw std::invalid_argument("maxBlockLength must be at least 1.");
+
+ _nodeMemory = _firstNode._memory;
+ _lastNode = &_firstNode;
+ }
+ ~Pool()
+ {
+ _Node *node = _firstNode._nextNode;
+ while (node)
+ {
+ _Node *nextNode = node->_nextNode;
+ delete node;
+ node = nextNode;
+ }
+ }
+
+ T *New()
+ {
+ if (_firstDeleted)
+ {
+ T *result = _firstDeleted;
+ _firstDeleted = *((T **)_firstDeleted);
+ new(result) T();
+ return result;
+ }
+
+ if (_countInNode >= _nodeCapacity)
+ _AllocateNewNode();
+
+ char *address = (char *)_nodeMemory;
+ address += _countInNode * _itemSize;
+ T *result = new(address) T();
+ _countInNode++;
+ return result;
+ }
+
+ // This method is useful if you want to call a non-default constructor.
+ // It should be used like this:
+ // new (pool.GetNextWithoutInitializing()) ObjectType(... parameters ...);
+ T *GetNextWithoutInitializing()
+ {
+ if (_firstDeleted)
+ {
+ T *result = (T *)_firstDeleted;
+ _firstDeleted = *((T **)_firstDeleted);
+ return result;
+ }
+
+ if (_countInNode >= _nodeCapacity)
+ _AllocateNewNode();
+
+ char *address = (char *)_nodeMemory;
+ address += _countInNode * _itemSize;
+ _countInNode++;
+ return (T *)address;
+ }
+ void Delete(T *content)
+ {
+ content->~T();
+
+ *((T **)content) = _firstDeleted;
+ _firstDeleted = content;
+ }
+ void DeleteWithoutDestroying(T *content)
+ {
+ *((T **)content) = _firstDeleted;
+ _firstDeleted = content;
+ }
+ };
+
+ template<typename T, class TMemoryAllocator>
+ const size_t Pool<T, TMemoryAllocator>::_itemSize = ((sizeof(T) + sizeof(void *) - 1) / sizeof(void *)) * sizeof(void *);
+
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_singleton.hpp b/src/libjin/common/je_singleton.hpp
new file mode 100644
index 0000000..d7f52c9
--- /dev/null
+++ b/src/libjin/common/je_singleton.hpp
@@ -0,0 +1,80 @@
+#ifndef __JE_SINGLETON_H__
+#define __JE_SINGLETON_H__
+
+namespace JinEngine
+{
+
+ ///
+ /// Singleton base class.
+ ///
+ template<class T>
+ class Singleton
+ {
+ public:
+ ///
+ /// Get singleton.
+ ///
+ /// @param Singleton instance of class.
+ ///
+ static T* get()
+ {
+ if (_instance == nullptr)
+ _instance = new T;
+ return _instance;
+ }
+
+ ///
+ /// Destroy instance of singleton.
+ ///
+ static void destroy()
+ {
+ delete _instance;
+ _instance = nullptr;
+ }
+
+ protected:
+ ///
+ /// Singleton constructor.
+ ///
+ Singleton() {};
+
+ ///
+ /// Singleton destructor.
+ ///
+ virtual ~Singleton() {};
+
+ ///
+ /// Singleton instance.
+ ///
+ static T* _instance;
+
+ private:
+ ///
+ /// Singleton copy constructor.
+ ///
+ /// @param singleton Singleton of class.
+ ///
+ Singleton(const Singleton& singleton);
+
+ ///
+ /// Singleton assignment.
+ ///
+ /// @param singleton Singleton of class.
+ ///
+ Singleton& operator = (const Singleton& singleton);
+
+ };
+
+ ///
+ /// Singleton instance.
+ ///
+ template<class T> T* Singleton<T>::_instance = nullptr;
+
+ ///
+ /// Singleton notation.
+ ///
+ #define singleton(T) friend Singleton<T>
+
+} // namespace JinEngine
+
+#endif // __JE_SINGLETON_H__
diff --git a/src/libjin/common/je_stringmap.hpp b/src/libjin/common/je_stringmap.hpp
new file mode 100644
index 0000000..7a3bd80
--- /dev/null
+++ b/src/libjin/common/je_stringmap.hpp
@@ -0,0 +1,143 @@
+#ifndef __JE_COMMON_SREINGMAP_H__
+#define __JE_COMMON_SREINGMAP_H__
+
+namespace JinEngine
+{
+
+ template<typename T, unsigned SIZE>
+ class StringMap
+ {
+ private:
+
+ struct Record
+ {
+ const char * key;
+ T value;
+ bool set;
+ Record() : set(false) {}
+ };
+
+ const static unsigned MAX = SIZE * 2;
+
+ Record records[MAX];
+ const char * reverse[SIZE];
+
+ public:
+
+ struct Entry
+ {
+ const char * key;
+ T value;
+ };
+
+ StringMap(Entry * entries, unsigned num)
+ {
+
+ for (unsigned i = 0; i < SIZE; ++i)
+ reverse[i] = 0;
+
+ unsigned n = num / sizeof(Entry);
+
+ for (unsigned i = 0; i < n; ++i)
+ {
+ add(entries[i].key, entries[i].value);
+ }
+ }
+
+ bool streq(const char * a, const char * b)
+ {
+ while (*a != 0 && *b != 0)
+ {
+ if (*a != *b)
+ return false;
+ ++a;
+ ++b;
+ }
+
+ return (*a == 0 && *b == 0);
+ }
+
+ bool find(const char * key, T & t)
+ {
+ //unsigned str_hash = djb2(key);
+
+ for (unsigned i = 0; i < MAX; ++i)
+ {
+ //unsigned str_i = (str_hash + i) % MAX; //this isn't used, is this intentional?
+
+ if (records[i].set && streq(records[i].key, key))
+ {
+ t = records[i].value;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool find(T key, const char *& str)
+ {
+ unsigned index = (unsigned)key;
+
+ if (index >= SIZE)
+ return false;
+
+ if (reverse[index] != 0)
+ {
+ str = reverse[index];
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ bool add(const char * key, T value)
+ {
+ unsigned str_hash = djb2(key);
+ bool inserted = false;
+
+ for (unsigned i = 0; i < MAX; ++i)
+ {
+ unsigned str_i = (str_hash + i) % MAX;
+
+ if (!records[str_i].set)
+ {
+ inserted = true;
+ records[str_i].set = true;
+ records[str_i].key = key;
+ records[str_i].value = value;
+ break;
+ }
+ }
+
+ unsigned index = (unsigned)value;
+
+ if (index >= SIZE)
+ {
+ printf("\nConstant %s out of bounds with %i!\n", key, index);
+ return false;
+ }
+
+ reverse[index] = key;
+
+ return inserted;
+ }
+
+ unsigned djb2(const char * key)
+ {
+ unsigned hash = 5381;
+ int c;
+
+ while ((c = *key++))
+ hash = ((hash << 5) + hash) + c;
+
+ return hash;
+ }
+
+ }; // StringMap
+
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_subsystem.hpp b/src/libjin/common/je_subsystem.hpp
new file mode 100644
index 0000000..c015bef
--- /dev/null
+++ b/src/libjin/common/je_subsystem.hpp
@@ -0,0 +1,78 @@
+#ifndef __JE_COMMON_SUBSYSTEM_H__
+#define __JE_COMMON_SUBSYSTEM_H__
+
+#include "../utils/je_macros.h"
+
+#include "je_singleton.hpp"
+
+namespace JinEngine
+{
+
+ ///
+ /// Subsystem class.
+ ///
+ template<class System>
+ class Subsystem : public Singleton<System>
+ {
+ public:
+ ///
+ /// Subsystem setting.
+ ///
+ struct Setting
+ {
+ };
+
+ typedef Setting SettingBase;
+
+ ///
+ /// Initialize subsystem.
+ ///
+ /// @param setting Subsystem setting.
+ /// @return True if initialize sucessful, otherwise return false.
+ ///
+ bool init(const SettingBase* setting = nullptr)
+ {
+ static bool success = initSystem(setting);
+ return success;
+ }
+
+ ///
+ /// Quit subsystem.
+ ///
+ void quit()
+ {
+ // Call only once.
+ static char __dummy__ = (quitSystem(), 1);
+ Singleton<System>::destroy();
+ }
+
+ protected:
+ singleton(System);
+
+ ///
+ /// Subsystem constructor.
+ ///
+ Subsystem() {};
+
+ ///
+ /// Subsystem destructor.
+ ///
+ virtual ~Subsystem()
+ {
+ };
+
+ ///
+ /// Initializer callback.
+ ///
+ virtual bool initSystem(const Setting* setting) = 0;
+
+ ///
+ /// Quit subsystem callback.
+ ///
+ virtual void quitSystem() = 0;
+
+ };
+
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_temporary.h b/src/libjin/common/je_temporary.h
new file mode 100644
index 0000000..647bfba
--- /dev/null
+++ b/src/libjin/common/je_temporary.h
@@ -0,0 +1,31 @@
+#ifndef __JE_TEMPORARY_H__
+#define __JE_TEMPORARY_H__
+
+namespace JinEngine
+{
+
+ ///
+ /// Class inherites this clound only be created on stack or static zone.
+ ///
+ class Temporary
+ {
+ public:
+ Temporary() {};
+ virtual ~Temporary() {};
+/*
+ protected:
+ void operator delete(void* t)
+ {
+ if(t != nullptr)
+ free(t);
+ }
+*/
+ private:
+ // Disable new operands.
+ void* operator new(size_t);
+
+ };
+
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_types.h b/src/libjin/common/je_types.h
new file mode 100644
index 0000000..e31ce5e
--- /dev/null
+++ b/src/libjin/common/je_types.h
@@ -0,0 +1,34 @@
+#ifndef __JE_TYPES_H__
+#define __JE_TYPES_H__
+#include <stdint.h>
+#include <stdlib.h>
+#include <cstring>
+
+namespace JinEngine
+{
+
+ typedef int8_t int8; ///< Signed integer with a size of 8 bits. Supports values from -128 to 127
+ typedef uint8_t uint8; ///< Unsigned integer with a size of 8 bits. Supports values from 0 to 255.
+ typedef uint8 byte; ///< Unsigned integer with 8 bits (1 byte). Supports 256 values from 0 to 255.
+ typedef int16_t int16; ///< Signed integer with a size of 16 bits. Supports values from -32768 to 32767
+ typedef uint16_t uint16; ///< Unsigned integer with a size of 16 bits. Supports values from 0 to 65535.
+ typedef int32_t int32; ///< Signed integer with a size of 32 bits. Supports values from -2147483648 to 2147483647.
+ typedef uint32_t uint32; ///< Unsigned integer with a size of 32 bits. Supports values from 0 to 4294967295, (2^32 - 1).
+ typedef int64_t int64; ///< Signed integer with a size of 64 bits. Supports values from -(2^63) to (2^63 - 1).
+ typedef uint64_t uint64; ///< Unsigned integer with a size of 64 bits, Supports values from 0 to (2^64 - 1).
+
+ typedef uint32_t uint;
+ typedef int32_t sint;
+
+#define Union(name, ...) \
+union _Ctor{ \
+ _Ctor() { memset(this, 0, sizeof(*this)); } \
+ __VA_ARGS__; \
+} name;
+
+#define Struct(name, ...) \
+struct {__VA_ARGS__;} name;
+
+}
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_utf8.cpp b/src/libjin/common/je_utf8.cpp
new file mode 100644
index 0000000..bd7ce94
--- /dev/null
+++ b/src/libjin/common/je_utf8.cpp
@@ -0,0 +1,42 @@
+#include "../core/je_configuration.h"
+#if jin_os == jin_os_windows
+
+#include "je_utf8.h"
+
+namespace JinEngine
+{
+
+ std::string to_utf8(LPCWSTR wstr)
+ {
+ size_t wide_len = wcslen(wstr) + 1;
+
+ // Get size in UTF-8.
+ int utf8_size = WideCharToMultiByte(CP_UTF8, 0, wstr, wide_len, 0, 0, 0, 0);
+
+ char * utf8_str = new char[utf8_size];
+
+ // Convert to UTF-8.
+ int ok = WideCharToMultiByte(CP_UTF8, 0, wstr, wide_len, utf8_str, utf8_size, 0, 0);
+
+ if (!ok)
+ {
+ delete[] utf8_str;
+ }
+
+ return ok ? std::string(utf8_str) : std::string();
+ }
+
+ void replace_char(std::string & str, char find, char replace)
+ {
+ int length = str.length();
+
+ for (int i = 0; i<length; i++)
+ {
+ if (str[i] == find)
+ str[i] = replace;
+ }
+ }
+
+} // namespace JinEngine
+
+#endif // jin_os == jin_os_windows \ No newline at end of file
diff --git a/src/libjin/common/je_utf8.h b/src/libjin/common/je_utf8.h
new file mode 100644
index 0000000..d840b75
--- /dev/null
+++ b/src/libjin/common/je_utf8.h
@@ -0,0 +1,34 @@
+#ifndef __JE_COMMON_UTF8_H__
+#define __JE_COMMON_UTF8_H__
+
+#include "../core/je_configuration.h"
+#if jin_os == jin_os_windows
+
+#include <string>
+#include <windows.h>
+
+namespace JinEngine
+{
+
+ ///
+ /// Convert the wide string to a UTF-8 encoded string.
+ ///
+ /// @param wstr The wide-char string.
+ /// @return A UTF-8 string.
+ ///
+ std::string to_utf8(LPCWSTR wstr);
+
+ ///
+ /// Replace all occurences of 'find' with 'replace' in a string.
+ ///
+ /// @param str The string to modify.
+ /// @param find The character to match.
+ /// @param replace The character to replace matches.
+ ///
+ void replace_char(std::string & str, char find, char replace);
+
+} // namespace JinEngine
+
+#endif // jin_os == jin_os_windows
+
+#endif // __JE_COMMON_UTF8_H__