[deploy] Merge pull request #54 from KiwanoEngine/dev
Add StringView and ConfigIni
This commit is contained in:
		
						commit
						394c74d5e5
					
				|  | @ -96,9 +96,9 @@ | |||
|     <ClInclude Include="..\..\src\kiwano\render\TextStyle.hpp" /> | ||||
|     <ClInclude Include="..\..\src\kiwano\render\Texture.h" /> | ||||
|     <ClInclude Include="..\..\src\kiwano\render\TextureCache.h" /> | ||||
|     <ClInclude Include="..\..\src\kiwano\utils\ConfigIni.h" /> | ||||
|     <ClInclude Include="..\..\src\kiwano\utils\EventTicker.h" /> | ||||
|     <ClInclude Include="..\..\src\kiwano\utils\Json.h" /> | ||||
|     <ClInclude Include="..\..\src\kiwano\utils\LocalStorage.h" /> | ||||
|     <ClInclude Include="..\..\src\kiwano\utils\Logger.h" /> | ||||
|     <ClInclude Include="..\..\src\kiwano\utils\ResourceCache.h" /> | ||||
|     <ClInclude Include="..\..\src\kiwano\utils\Task.h" /> | ||||
|  | @ -174,8 +174,8 @@ | |||
|     <ClCompile Include="..\..\src\kiwano\render\TextLayout.cpp" /> | ||||
|     <ClCompile Include="..\..\src\kiwano\render\Texture.cpp" /> | ||||
|     <ClCompile Include="..\..\src\kiwano\render\TextureCache.cpp" /> | ||||
|     <ClCompile Include="..\..\src\kiwano\utils\ConfigIni.cpp" /> | ||||
|     <ClCompile Include="..\..\src\kiwano\utils\EventTicker.cpp" /> | ||||
|     <ClCompile Include="..\..\src\kiwano\utils\LocalStorage.cpp" /> | ||||
|     <ClCompile Include="..\..\src\kiwano\utils\Logger.cpp" /> | ||||
|     <ClCompile Include="..\..\src\kiwano\utils\ResourceCache.cpp" /> | ||||
|     <ClCompile Include="..\..\src\kiwano\utils\Task.cpp" /> | ||||
|  |  | |||
|  | @ -105,9 +105,6 @@ | |||
|     <ClInclude Include="..\..\src\kiwano\2d\action\ActionWalk.h"> | ||||
|       <Filter>2d\action</Filter> | ||||
|     </ClInclude> | ||||
|     <ClInclude Include="..\..\src\kiwano\utils\LocalStorage.h"> | ||||
|       <Filter>utils</Filter> | ||||
|     </ClInclude> | ||||
|     <ClInclude Include="..\..\src\kiwano\utils\UserData.h"> | ||||
|       <Filter>utils</Filter> | ||||
|     </ClInclude> | ||||
|  | @ -351,6 +348,9 @@ | |||
|     <ClInclude Include="..\..\src\kiwano\base\component\ComponentManager.h"> | ||||
|       <Filter>base\component</Filter> | ||||
|     </ClInclude> | ||||
|     <ClInclude Include="..\..\src\kiwano\utils\ConfigIni.h"> | ||||
|       <Filter>utils</Filter> | ||||
|     </ClInclude> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClCompile Include="..\..\src\kiwano\2d\Canvas.cpp"> | ||||
|  | @ -401,9 +401,6 @@ | |||
|     <ClCompile Include="..\..\src\kiwano\2d\action\ActionWalk.cpp"> | ||||
|       <Filter>2d\action</Filter> | ||||
|     </ClCompile> | ||||
|     <ClCompile Include="..\..\src\kiwano\utils\LocalStorage.cpp"> | ||||
|       <Filter>utils</Filter> | ||||
|     </ClCompile> | ||||
|     <ClCompile Include="..\..\src\kiwano\utils\UserData.cpp"> | ||||
|       <Filter>utils</Filter> | ||||
|     </ClCompile> | ||||
|  | @ -575,6 +572,9 @@ | |||
|     <ClCompile Include="..\..\src\kiwano\base\component\ComponentManager.cpp"> | ||||
|       <Filter>base\component</Filter> | ||||
|     </ClCompile> | ||||
|     <ClCompile Include="..\..\src\kiwano\utils\ConfigIni.cpp"> | ||||
|       <Filter>utils</Filter> | ||||
|     </ClCompile> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <None Include="suppress_warning.ruleset" /> | ||||
|  |  | |||
|  | @ -20,17 +20,24 @@ | |||
| 
 | ||||
| #pragma once | ||||
| #include <string> | ||||
| #include <type_traits> | ||||
| 
 | ||||
| namespace kiwano | ||||
| { | ||||
| 
 | ||||
| /// \~chinese
 | ||||
| /// @brief »ù´¡×Ö·û´®ÈÝÆ÷
 | ||||
| template <typename CharTy> | ||||
| using BasicString = std::basic_string<CharTy>; | ||||
| 
 | ||||
| /// \~chinese
 | ||||
| /// @brief 字符串容器
 | ||||
| using String = std::string; | ||||
| using String = BasicString<char>; | ||||
| 
 | ||||
| /// \~chinese
 | ||||
| /// @brief 宽字符串容器
 | ||||
| using WideString = std::wstring; | ||||
| using WideString = BasicString<wchar_t>; | ||||
| 
 | ||||
| 
 | ||||
| namespace strings | ||||
| { | ||||
|  | @ -53,4 +60,351 @@ WideString NarrowToWide(const String& str); | |||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /// \~chinese
 | ||||
| /// @brief »ù´¡³£×Ö·û´®ÊÓͼ
 | ||||
| template <typename CharTy> | ||||
| class BasicStringView | ||||
| { | ||||
| public: | ||||
|     using value_type = CharTy; | ||||
|     using pointer = CharTy*; | ||||
|     using const_pointer = const CharTy*; | ||||
|     using reference = CharTy&; | ||||
|     using const_reference = const CharTy&; | ||||
|     using traits_type = std::char_traits<CharTy>; | ||||
|     using size_type = std::size_t; | ||||
|     using string_type = BasicString<CharTy>; | ||||
| 
 | ||||
|     BasicStringView() | ||||
|         : ptr_(nullptr) | ||||
|         , count_(0) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     BasicStringView(const_pointer c_str) | ||||
|     { | ||||
|         ptr_ = c_str; | ||||
|         if (c_str) | ||||
|         { | ||||
|             count_ = traits_type::length(c_str); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             count_ = 0; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     BasicStringView(const_pointer c_str, size_type count) | ||||
|     { | ||||
|         ptr_ = c_str; | ||||
|         if (c_str) | ||||
|         { | ||||
|             count_ = count; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             count_ = 0; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     BasicStringView(const string_type& str) | ||||
|         : ptr_(str.c_str()) | ||||
|         , count_(str.length()) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     BasicStringView(const BasicStringView& rhs) | ||||
|         : ptr_(rhs.ptr_) | ||||
|         , count_(rhs.count_) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     inline const value_type* Data() const | ||||
|     { | ||||
|         return ptr_; | ||||
|     } | ||||
| 
 | ||||
|     inline bool IsEmpty() const | ||||
|     { | ||||
|         return !ptr_ || !count_; | ||||
|     } | ||||
| 
 | ||||
|     inline size_type Find(value_type ch) const | ||||
|     { | ||||
|         const auto ptr = traits_type::find(ptr_, count_, ch); | ||||
|         if (ptr) | ||||
|         { | ||||
|             return ptr - ptr_; | ||||
|         } | ||||
|         return string_type::npos; | ||||
|     } | ||||
| 
 | ||||
|     inline BasicStringView SubStr(size_type pos, size_type count = string_type::npos) const | ||||
|     { | ||||
|         if (pos >= count_) | ||||
|             return BasicStringView(); | ||||
| 
 | ||||
|         if (count == string_type::npos) | ||||
|             return BasicStringView(ptr_ + pos, count_ - pos); | ||||
| 
 | ||||
|         KGE_ASSERT(pos + count <= count_); | ||||
|         return BasicStringView(ptr_ + pos, count); | ||||
|     } | ||||
| 
 | ||||
|     inline size_type GetLength() const | ||||
|     { | ||||
|         return count_; | ||||
|     } | ||||
| 
 | ||||
|     inline value_type At(size_type index) const | ||||
|     { | ||||
|         return operator[](index); | ||||
|     } | ||||
| 
 | ||||
|     inline value_type operator[](size_type index) const | ||||
|     { | ||||
|         if (IsEmpty() || index >= count_) | ||||
|             throw std::out_of_range("operator[] out of index"); | ||||
|         return ptr_[index]; | ||||
|     } | ||||
| 
 | ||||
|     inline operator string_type() const | ||||
|     { | ||||
|         return string_type(ptr_, count_); | ||||
|     } | ||||
| 
 | ||||
|     inline BasicStringView& operator=(const BasicStringView& rhs) | ||||
|     { | ||||
|         ptr_ = rhs.ptr_; | ||||
|         count_ = rhs.count_; | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
| public: | ||||
|     //
 | ||||
|     // Iterators for BasicStringView
 | ||||
|     //
 | ||||
|     class Iterator | ||||
|     { | ||||
|         const value_type* ptr_; | ||||
|         size_type         pos_; | ||||
|         size_type         count_; | ||||
| 
 | ||||
|     public: | ||||
|         using iterator_category = std::random_access_iterator_tag; | ||||
|         using value_type        = value_type; | ||||
|         using pointer           = value_type*; | ||||
|         using reference         = value_type&; | ||||
|         using difference_type   = ptrdiff_t; | ||||
| 
 | ||||
|         inline Iterator(pointer ptr, size_type pos, size_type count) | ||||
|             : ptr_(ptr) | ||||
|             , pos_(pos) | ||||
|             , count_(count) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         inline Iterator(const Iterator& rhs) | ||||
|             : ptr_(rhs.ptr_) | ||||
|             , pos_(rhs.pos_) | ||||
|             , count_(rhs.count_) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         inline Iterator& operator=(const Iterator& rhs) | ||||
|         { | ||||
|             ptr_ = rhs.ptr_; | ||||
|             pos_ = rhs.pos_; | ||||
|             count_ = rhs.count_; | ||||
|             return *this; | ||||
|         } | ||||
| 
 | ||||
|         inline const value_type& operator*() const | ||||
|         { | ||||
|             KGE_ASSERT(pos_ < count_); | ||||
|             return ptr_[pos_]; | ||||
|         } | ||||
| 
 | ||||
|         inline const value_type* operator->() const | ||||
|         { | ||||
|             return std::pointer_traits<pointer>::pointer_to(**this); | ||||
|         } | ||||
| 
 | ||||
|         inline Iterator& operator+=(size_type count) | ||||
|         { | ||||
|             KGE_ASSERT(pos_ + count >= 0 && pos_ + count <= count_); | ||||
|             pos_ += count; | ||||
|             return (*this); | ||||
|         } | ||||
| 
 | ||||
|         inline Iterator& operator-=(size_type count) | ||||
|         { | ||||
|             KGE_ASSERT(pos_ - count >= 0 && pos_ - count <= count_); | ||||
|             pos_ -= count; | ||||
|             return (*this); | ||||
|         } | ||||
| 
 | ||||
|         inline const Iterator operator+(size_type count) const | ||||
|         { | ||||
|             Iterator iter(*this); | ||||
|             iter += count; | ||||
|             return iter; | ||||
|         } | ||||
| 
 | ||||
|         inline const Iterator& operator-(size_type count) const | ||||
|         { | ||||
|             Iterator iter(*this); | ||||
|             iter -= count; | ||||
|             return iter; | ||||
|         } | ||||
| 
 | ||||
|         inline Iterator& operator++() | ||||
|         { | ||||
|             KGE_ASSERT(pos_ < count_); | ||||
|             ++pos_; | ||||
|             return (*this); | ||||
|         } | ||||
| 
 | ||||
|         inline Iterator operator++(int) | ||||
|         { | ||||
|             Iterator old = (*this); | ||||
|             ++(*this); | ||||
|             return old; | ||||
|         } | ||||
| 
 | ||||
|         inline Iterator& operator--() | ||||
|         { | ||||
|             KGE_ASSERT(pos_ > 0); | ||||
|             --pos_; | ||||
|             return (*this); | ||||
|         } | ||||
| 
 | ||||
|         inline Iterator operator--(int) | ||||
|         { | ||||
|             Iterator old = (*this); | ||||
|             --(*this); | ||||
|             return old; | ||||
|         } | ||||
| 
 | ||||
|         inline const value_type& operator[](size_type index) const | ||||
|         { | ||||
|             Iterator iter = (*this + index); | ||||
|             return iter.ptr_[iter.pos_]; | ||||
|         } | ||||
| 
 | ||||
|         inline difference_type operator-(const Iterator& other) const | ||||
|         { | ||||
|             KGE_ASSERT(ptr_ == other.ptr_ && count_ == other.count_); | ||||
|             return static_cast<difference_type>(pos_ - other.pos_); | ||||
|         } | ||||
| 
 | ||||
|         inline bool operator==(const Iterator& other) const | ||||
|         { | ||||
|             return ptr_ == other.ptr_ && pos_ == other.pos_ && count_ == other.count_; | ||||
|         } | ||||
| 
 | ||||
|         inline bool operator!=(const Iterator& other) const | ||||
|         { | ||||
|             return !(*this == other); | ||||
|         } | ||||
| 
 | ||||
|         inline bool operator<(const Iterator& other) const | ||||
|         { | ||||
|             return ptr_ < other.ptr_ || pos_ < other.pos_ || count_ < other.count_; | ||||
|         } | ||||
| 
 | ||||
|         inline bool operator<=(const Iterator& other) const | ||||
|         { | ||||
|             return (*this < other) || (*this == other); | ||||
|         } | ||||
| 
 | ||||
|         inline bool operator>(const Iterator& other) const | ||||
|         { | ||||
|             return !(*this <= other); | ||||
|         } | ||||
| 
 | ||||
|         inline bool operator>=(const Iterator& other) const | ||||
|         { | ||||
|             return !(*this < other); | ||||
|         } | ||||
| 
 | ||||
|         inline operator bool() const | ||||
|         { | ||||
|             return ptr_ != nullptr && pos_ != count_; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     using const_iterator         = Iterator; | ||||
|     using iterator               = const_iterator; | ||||
|     using const_reverse_iterator = std::reverse_iterator<const_iterator>; | ||||
|     using reverse_iterator       = const_reverse_iterator; | ||||
| 
 | ||||
|     inline const_iterator begin() const | ||||
|     { | ||||
|         return const_iterator(ptr_, 0, count_); | ||||
|     } | ||||
| 
 | ||||
|     inline const_iterator cbegin() const | ||||
|     { | ||||
|         return begin(); | ||||
|     } | ||||
| 
 | ||||
|     inline const_iterator end() const | ||||
|     { | ||||
|         return const_iterator(ptr_, count_, count_); | ||||
|     } | ||||
| 
 | ||||
|     inline const_iterator cend() const | ||||
|     { | ||||
|         return end(); | ||||
|     } | ||||
| 
 | ||||
|     inline const_reverse_iterator rbegin() const | ||||
|     { | ||||
|         return const_reverse_iterator(end()); | ||||
|     } | ||||
| 
 | ||||
|     inline const_reverse_iterator crbegin() const | ||||
|     { | ||||
|         return rbegin(); | ||||
|     } | ||||
| 
 | ||||
|     inline const_reverse_iterator rend() const | ||||
|     { | ||||
|         return const_reverse_iterator(begin()); | ||||
|     } | ||||
| 
 | ||||
|     inline const_reverse_iterator crend() const | ||||
|     { | ||||
|         return rend(); | ||||
|     } | ||||
| 
 | ||||
|     inline const value_type& front() const | ||||
|     { | ||||
|         if (IsEmpty()) | ||||
|             throw std::out_of_range("front() called on empty list"); | ||||
|         return ptr_[0]; | ||||
|     } | ||||
| 
 | ||||
|     inline const value_type& back() const | ||||
|     { | ||||
|         if (IsEmpty()) | ||||
|             throw std::out_of_range("back() called on empty list"); | ||||
|         return ptr_[count_]; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     const value_type* ptr_; | ||||
|     size_type         count_; | ||||
| }; | ||||
| 
 | ||||
| /// \~chinese
 | ||||
| /// @brief ×Ö·û´®ÊÓͼ
 | ||||
| using StringView = BasicStringView<char>; | ||||
| 
 | ||||
| /// \~chinese
 | ||||
| /// @brief ¿í×Ö·û´®ÊÓͼ
 | ||||
| using WideStringView = BasicStringView<wchar_t>; | ||||
| 
 | ||||
| }  // namespace kiwano
 | ||||
|  |  | |||
|  | @ -263,17 +263,17 @@ inline void Duration::SetMilliseconds(int64_t ms) | |||
| 
 | ||||
| inline void Duration::SetSeconds(float seconds) | ||||
| { | ||||
|     milliseconds_ = static_cast<long>(seconds * 1000.f); | ||||
|     milliseconds_ = static_cast<int64_t>(seconds * 1000.f); | ||||
| } | ||||
| 
 | ||||
| inline void Duration::SetMinutes(float minutes) | ||||
| { | ||||
|     milliseconds_ = static_cast<long>(minutes * 60 * 1000.f); | ||||
|     milliseconds_ = static_cast<int64_t>(minutes * 60 * 1000.f); | ||||
| } | ||||
| 
 | ||||
| inline void Duration::SetHours(float hours) | ||||
| { | ||||
|     milliseconds_ = static_cast<long>(hours * 60 * 60 * 1000.f); | ||||
|     milliseconds_ = static_cast<int64_t>(hours * 60 * 60 * 1000.f); | ||||
| } | ||||
| 
 | ||||
| inline bool Time::IsZero() const | ||||
|  |  | |||
|  | @ -127,7 +127,6 @@ | |||
| //
 | ||||
| 
 | ||||
| #include <kiwano/utils/Logger.h> | ||||
| #include <kiwano/utils/LocalStorage.h> | ||||
| #include <kiwano/utils/ResourceCache.h> | ||||
| #include <kiwano/utils/UserData.h> | ||||
| #include <kiwano/utils/Timer.h> | ||||
|  | @ -135,3 +134,4 @@ | |||
| #include <kiwano/utils/EventTicker.h> | ||||
| #include <kiwano/utils/Task.h> | ||||
| #include <kiwano/utils/TaskScheduler.h> | ||||
| #include <kiwano/utils/ConfigIni.h> | ||||
|  |  | |||
|  | @ -42,9 +42,6 @@ Application::Application() | |||
|     Use(Renderer::GetInstance()); | ||||
|     Use(Input::GetInstance()); | ||||
|     Use(Director::GetInstance()); | ||||
| 
 | ||||
|     ticker_ = Ticker::Create(0); | ||||
|     ticker_->Tick(); | ||||
| } | ||||
| 
 | ||||
| Application::~Application() | ||||
|  | @ -76,11 +73,27 @@ void Application::Run(RunnerPtr runner, bool debug) | |||
| 
 | ||||
|     while (running_) | ||||
|     { | ||||
|         if (ticker_->Tick()) | ||||
|         if (!frame_ticker_) | ||||
|         { | ||||
|             if (!runner->MainLoop(ticker_->GetDeltaTime())) | ||||
|             frame_ticker_ = Ticker::Create(0); | ||||
|         } | ||||
| 
 | ||||
|         if (frame_ticker_->Tick()) | ||||
|         { | ||||
|             // Execute main loop
 | ||||
|             if (!runner->MainLoop(frame_ticker_->GetDeltaTime())) | ||||
|                 running_ = false; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Releases CPU
 | ||||
|             Duration total_dt = frame_ticker_->GetDeltaTime() + frame_ticker_->GetErrorTime(); | ||||
|             Duration sleep_dt = frame_ticker_->GetInterval() - total_dt; | ||||
|             if (sleep_dt.Milliseconds() > 1LL) | ||||
|             { | ||||
|                 sleep_dt.Sleep(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     this->Destroy(); | ||||
|  | @ -89,19 +102,17 @@ void Application::Run(RunnerPtr runner, bool debug) | |||
| void Application::Pause() | ||||
| { | ||||
|     is_paused_ = true; | ||||
|     if (ticker_) | ||||
|     { | ||||
|         ticker_->Pause(); | ||||
|     } | ||||
| 
 | ||||
|     if (frame_ticker_) | ||||
|         frame_ticker_->Pause(); | ||||
| } | ||||
| 
 | ||||
| void Application::Resume() | ||||
| { | ||||
|     is_paused_ = false; | ||||
|     if (ticker_) | ||||
|     { | ||||
|         ticker_->Resume(); | ||||
|     } | ||||
| 
 | ||||
|     if (frame_ticker_) | ||||
|         frame_ticker_->Resume(); | ||||
| } | ||||
| 
 | ||||
| void Application::Quit() | ||||
|  |  | |||
|  | @ -78,6 +78,19 @@ public: | |||
|      */ | ||||
|     void Quit(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * \~chinese | ||||
|      * @brief 获取暂停状态 | ||||
|      */ | ||||
|     bool IsPaused() const; | ||||
| 
 | ||||
|     /**
 | ||||
|      * \~chinese | ||||
|      * @brief 添加模块 | ||||
|      * @param[in] module 模块 | ||||
|      */ | ||||
|     void Use(Module& module); | ||||
| 
 | ||||
|     /**
 | ||||
|      * \~chinese | ||||
|      * @brief 获取程序运行器 | ||||
|  | @ -92,22 +105,15 @@ public: | |||
| 
 | ||||
|     /**
 | ||||
|      * \~chinese | ||||
|      * @brief 获取报时器 | ||||
|      * @brief 获取帧报时器 | ||||
|      */ | ||||
|     TickerPtr GetTicker() const; | ||||
|     TickerPtr GetFrameTicker() const; | ||||
| 
 | ||||
|     /**
 | ||||
|      * \~chinese | ||||
|      * @brief 获取暂停状态 | ||||
|      * @brief 设置帧报时器 | ||||
|      */ | ||||
|     bool IsPaused() const; | ||||
| 
 | ||||
|     /**
 | ||||
|      * \~chinese | ||||
|      * @brief 添加模块 | ||||
|      * @param[in] module 模块 | ||||
|      */ | ||||
|     void Use(Module& module); | ||||
|     void SetFrameTicker(TickerPtr ticker); | ||||
| 
 | ||||
|     /**
 | ||||
|      * \~chinese | ||||
|  | @ -166,7 +172,7 @@ private: | |||
|     bool                    is_paused_; | ||||
|     float                   time_scale_; | ||||
|     RunnerPtr               runner_; | ||||
|     TickerPtr               ticker_; | ||||
|     TickerPtr               frame_ticker_; | ||||
|     List<Module*>           modules_; | ||||
|     std::mutex              perform_mutex_; | ||||
|     Queue<Function<void()>> functions_to_perform_; | ||||
|  | @ -183,9 +189,14 @@ inline WindowPtr Application::GetMainWindow() const | |||
|     return runner_->GetMainWindow(); | ||||
| } | ||||
| 
 | ||||
| inline TickerPtr Application::GetTicker() const | ||||
| inline TickerPtr Application::GetFrameTicker() const | ||||
| { | ||||
|     return ticker_; | ||||
|     return frame_ticker_; | ||||
| } | ||||
| 
 | ||||
| inline void Application::SetFrameTicker(TickerPtr ticker) | ||||
| { | ||||
|     frame_ticker_ = ticker; | ||||
| } | ||||
| 
 | ||||
| inline bool Application::IsPaused() const | ||||
|  |  | |||
|  | @ -34,6 +34,8 @@ | |||
| #include <Windowsx.h>  // GET_X_LPARAM, GET_Y_LPARAM
 | ||||
| #include <imm.h>       // ImmAssociateContext
 | ||||
| #pragma comment(lib, "imm32.lib") | ||||
| #include <timeapi.h>  // timeBeginPeriod, timeEndPeriod
 | ||||
| #pragma comment(lib, "winmm.lib") | ||||
| 
 | ||||
| namespace kiwano | ||||
| { | ||||
|  | @ -185,6 +187,8 @@ WindowWin32Impl::WindowWin32Impl() | |||
|     // F1 - F12
 | ||||
|     for (size_t i = 0; i < 12; ++i) | ||||
|         key_map_[VK_F1 + i] = KeyCode(size_t(KeyCode::F1) + i); | ||||
| 
 | ||||
|     ::timeBeginPeriod(0); | ||||
| } | ||||
| 
 | ||||
| WindowWin32Impl::~WindowWin32Impl() | ||||
|  | @ -194,6 +198,8 @@ WindowWin32Impl::~WindowWin32Impl() | |||
|         ::DestroyWindow(handle_); | ||||
|         handle_ = nullptr; | ||||
|     } | ||||
| 
 | ||||
|     ::timeEndPeriod(0); | ||||
| } | ||||
| 
 | ||||
| void WindowWin32Impl::Init(const String& title, uint32_t width, uint32_t height, uint32_t icon, bool resizable) | ||||
|  |  | |||
|  | @ -0,0 +1,372 @@ | |||
| // Copyright (c) 2016-2018 Kiwano - Nomango
 | ||||
| //
 | ||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy
 | ||||
| // of this software and associated documentation files (the "Software"), to deal
 | ||||
| // in the Software without restriction, including without limitation the rights
 | ||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | ||||
| // copies of the Software, and to permit persons to whom the Software is
 | ||||
| // furnished to do so, subject to the following conditions:
 | ||||
| //
 | ||||
| // The above copyright notice and this permission notice shall be included in
 | ||||
| // all copies or substantial portions of the Software.
 | ||||
| //
 | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | ||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | ||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | ||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | ||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | ||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | ||||
| // THE SOFTWARE.
 | ||||
| 
 | ||||
| #include <kiwano/utils/ConfigIni.h> | ||||
| #include <kiwano/core/Exception.h> | ||||
| #include <fstream>  // std::ifstream, std::ofstream
 | ||||
| #include <algorithm>  // std::sort, std::for_each
 | ||||
| #include <cctype>  // std::isspace
 | ||||
| 
 | ||||
| #define KGE_DEFAULT_INI_SECTION_NAME "default" | ||||
| 
 | ||||
| namespace kiwano | ||||
| { | ||||
| 
 | ||||
| StringView Trim(StringView str) | ||||
| { | ||||
|     if (!str.IsEmpty()) | ||||
|     { | ||||
|         std::size_t start = 0, end = str.GetLength(); | ||||
|         while (std::isspace(str[start])) | ||||
|             ++start; | ||||
|         while (std::isspace(str[end - 1])) | ||||
|             --end; | ||||
| 
 | ||||
|         if (end - start) | ||||
|             return StringView(str.Data() + start, end - start); | ||||
|     } | ||||
|     return StringView(); | ||||
| } | ||||
| 
 | ||||
| class IniParser | ||||
| { | ||||
|     StringView line_; | ||||
| public: | ||||
|     IniParser(StringView line) | ||||
|         : line_(line) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     bool ClearComment() | ||||
|     { | ||||
|         auto pos = line_.Find(';'); | ||||
|         if (pos != String::npos) | ||||
|         { | ||||
|             if (pos == 0) | ||||
|                 return true; | ||||
| 
 | ||||
|             if (std::isspace(line_[pos - 1])) | ||||
|             { | ||||
|                 line_ = Trim(line_.SubStr(0, pos - 1)); | ||||
|                 return line_.IsEmpty(); | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     bool IsSection() const | ||||
|     { | ||||
|         return line_[0] == '[' && line_.GetLength() > 2 && line_[line_.GetLength() - 1] == ']'; | ||||
|     } | ||||
| 
 | ||||
|     StringView GetSectionName() const | ||||
|     { | ||||
|         return Trim(line_.SubStr(1, line_.GetLength() - 2)); | ||||
|     } | ||||
| 
 | ||||
|     bool GetKeyValue(StringView* key, StringView* value) | ||||
|     { | ||||
|         auto pos = line_.Find('='); | ||||
|         if (pos == String::npos) | ||||
|             return false; | ||||
| 
 | ||||
|         *key = Trim(line_.SubStr(0, pos)); | ||||
|         *value = Trim(line_.SubStr(pos + 1)); | ||||
| 
 | ||||
|         return !(*key).IsEmpty() && !(*value).IsEmpty(); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| ConfigIniPtr ConfigIni::Create(const String& file_path) | ||||
| { | ||||
|     ConfigIniPtr ptr = memory::New<ConfigIni>(); | ||||
|     if (ptr) | ||||
|     { | ||||
|         if (!ptr->Load(file_path)) | ||||
|         { | ||||
|             return nullptr; | ||||
|         } | ||||
|     } | ||||
|     return ptr; | ||||
| } | ||||
| 
 | ||||
| bool ConfigIni::Load(const String& file_path) | ||||
| { | ||||
|     std::ifstream ifs(file_path); | ||||
| 
 | ||||
|     if (ifs.is_open()) | ||||
|     { | ||||
|         return Load(ifs); | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool ConfigIni::Load(std::istream& istream) | ||||
| { | ||||
|     try | ||||
|     { | ||||
|         String section = KGE_DEFAULT_INI_SECTION_NAME; | ||||
|         for (String line; std::getline(istream, line);) | ||||
|         { | ||||
|             ParseLine(line, §ion); | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|     catch (Exception) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool ConfigIni::Save(const String& file_path) | ||||
| { | ||||
|     std::ofstream ofs(file_path); | ||||
| 
 | ||||
|     if (ofs.is_open()) | ||||
|     { | ||||
|         return Save(ofs); | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool ConfigIni::Save(std::ostream& os) | ||||
| { | ||||
|     // Get all keys
 | ||||
|     Vector<String> keys; | ||||
|     keys.reserve(sections_.size()); | ||||
|     std::for_each(sections_.begin(), sections_.end(), [&](SectionMap::value_type& pair) { keys.push_back(pair.first); }); | ||||
| 
 | ||||
|     // Sort for keys
 | ||||
|     std::sort(keys.begin(), keys.end()); | ||||
| 
 | ||||
|     // Output to ini
 | ||||
|     for (const auto& key : keys) | ||||
|     { | ||||
|         os << '[' << key << ']' << std::endl; | ||||
|         for (const auto& pair : sections_[key]) | ||||
|         { | ||||
|             os << pair.first << " = " << pair.second << std::endl; | ||||
|         } | ||||
|         os << std::endl; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| ConfigIni::SectionMap ConfigIni::GetSectionMap() const | ||||
| { | ||||
|     return sections_; | ||||
| } | ||||
| 
 | ||||
| ConfigIni::ValueMap ConfigIni::GetSection(const String& section) const | ||||
| { | ||||
|     auto iter = sections_.find(section); | ||||
|     if (iter != sections_.end()) | ||||
|         return iter->second; | ||||
|     return ValueMap(); | ||||
| } | ||||
| 
 | ||||
| String ConfigIni::GetString(const String& section_name, const String& key) const | ||||
| { | ||||
|     if (HasSection(section_name)) | ||||
|     { | ||||
|         const auto& section = sections_.at(section_name); | ||||
| 
 | ||||
|         auto iter_key = section.find(key); | ||||
|         if (iter_key != section.end()) | ||||
|             return iter_key->second; | ||||
|     } | ||||
|     return String(); | ||||
| } | ||||
| 
 | ||||
| float ConfigIni::GetFloat(const String& section, const String& key, float default_value) const | ||||
| { | ||||
|     String str = GetString(section, key); | ||||
|     if (str.empty()) | ||||
|         return default_value; | ||||
| 
 | ||||
|     try | ||||
|     { | ||||
|         std::size_t pos   = 0; | ||||
|         float       value = std::stof(str, &pos); | ||||
|         if (pos == str.size()) | ||||
|             return value; | ||||
|     } | ||||
|     catch (std::invalid_argument) | ||||
|     { | ||||
|         return default_value; | ||||
|     } | ||||
|     return default_value; | ||||
| } | ||||
| 
 | ||||
| double ConfigIni::GetDouble(const String& section, const String& key, double default_value) const | ||||
| { | ||||
|     String str = GetString(section, key); | ||||
|     if (str.empty()) | ||||
|         return default_value; | ||||
| 
 | ||||
|     try | ||||
|     { | ||||
|         std::size_t pos   = 0; | ||||
|         double      value = std::stod(str, &pos); | ||||
|         if (pos == str.size()) | ||||
|             return value; | ||||
|     } | ||||
|     catch (std::invalid_argument) | ||||
|     { | ||||
|         return default_value; | ||||
|     } | ||||
|     return default_value; | ||||
| } | ||||
| 
 | ||||
| int ConfigIni::GetInt(const String& section, const String& key, int default_value) const | ||||
| { | ||||
|     String str = GetString(section, key); | ||||
|     if (str.empty()) | ||||
|         return default_value; | ||||
| 
 | ||||
|     try | ||||
|     { | ||||
|         std::size_t pos   = 0; | ||||
|         int         value = std::stoi(str, &pos); | ||||
|         if (pos == str.size()) | ||||
|             return value; | ||||
|     } | ||||
|     catch (std::invalid_argument) | ||||
|     { | ||||
|         return default_value; | ||||
|     } | ||||
|     return default_value; | ||||
| } | ||||
| 
 | ||||
| bool ConfigIni::GetBool(const String& section, const String& key, bool default_value) const | ||||
| { | ||||
|     String str = GetString(section, key); | ||||
|     if (!str.empty()) | ||||
|     { | ||||
|         if (str == "true" || str == "1") | ||||
|             return true; | ||||
|         else if (str == "false" || str == "0") | ||||
|             return false; | ||||
|     } | ||||
|     return default_value; | ||||
| } | ||||
| 
 | ||||
| bool ConfigIni::HasSection(const String& section) const | ||||
| { | ||||
|     return !!sections_.count(section); | ||||
| } | ||||
| 
 | ||||
| bool ConfigIni::HasValue(const String& section, const String& key) const | ||||
| { | ||||
|     if (HasSection(section)) | ||||
|     { | ||||
|         return !!sections_.at(section).count(section); | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void ConfigIni::SetSectionMap(const SectionMap& sections) | ||||
| { | ||||
|     sections_ = sections; | ||||
| } | ||||
| 
 | ||||
| void ConfigIni::SetSection(const String& section, const ValueMap& values) | ||||
| { | ||||
|     sections_.insert(std::make_pair(section, values)); | ||||
| } | ||||
| 
 | ||||
| void ConfigIni::SetString(const String& section, const String& key, const String& value) | ||||
| { | ||||
|     if (HasSection(section)) | ||||
|         sections_[section].insert(std::make_pair(key, value)); | ||||
|     else | ||||
|         SetSection(section, ValueMap{ { key, value } }); | ||||
| } | ||||
| 
 | ||||
| void ConfigIni::SetFloat(const String& section, const String& key, float value) | ||||
| { | ||||
|     String str = std::to_string(value); | ||||
|     SetString(section, key, str); | ||||
| } | ||||
| 
 | ||||
| void ConfigIni::SetDouble(const String& section, const String& key, double value) | ||||
| { | ||||
|     String str = std::to_string(value); | ||||
|     SetString(section, key, str); | ||||
| } | ||||
| 
 | ||||
| void ConfigIni::SetInt(const String& section, const String& key, int value) | ||||
| { | ||||
|     String str = std::to_string(value); | ||||
|     SetString(section, key, str); | ||||
| } | ||||
| 
 | ||||
| void ConfigIni::SetBool(const String& section, const String& key, bool value) | ||||
| { | ||||
|     SetString(section, key, value ? "true" : "false"); | ||||
| } | ||||
| 
 | ||||
| ConfigIni::ValueMap& ConfigIni::operator[](const String& section) | ||||
| { | ||||
|     if (!HasSection(section)) | ||||
|     { | ||||
|         sections_.insert(std::make_pair(section, ValueMap())); | ||||
|     } | ||||
|     return sections_[section]; | ||||
| } | ||||
| 
 | ||||
| const ConfigIni::ValueMap& ConfigIni::operator[](const String& section) const | ||||
| { | ||||
|     if (!HasSection(section)) | ||||
|     { | ||||
|         const_cast<SectionMap&>(sections_).insert(std::make_pair(section, ValueMap())); | ||||
|     } | ||||
|     return sections_.at(section); | ||||
| } | ||||
| 
 | ||||
| void ConfigIni::ParseLine(StringView line, String* section) | ||||
| { | ||||
|     line = Trim(line); | ||||
|     if (line.IsEmpty()) | ||||
|         return; | ||||
| 
 | ||||
|     IniParser parser(line); | ||||
|     if (parser.ClearComment()) | ||||
|         return; | ||||
| 
 | ||||
|     if (parser.IsSection()) | ||||
|     { | ||||
|         auto name = parser.GetSectionName(); | ||||
|         if (name.IsEmpty()) | ||||
|             throw Exception("Empty section name"); | ||||
|         *section = name; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     StringView key, value; | ||||
|     if (!parser.GetKeyValue(&key, &value)) | ||||
|     { | ||||
|         throw Exception("Parse key-value failed"); | ||||
|     } | ||||
|     SetString(*section, key, value); | ||||
| } | ||||
| 
 | ||||
| }  // namespace kiwano
 | ||||
|  | @ -0,0 +1,179 @@ | |||
| // Copyright (c) 2016-2018 Kiwano - Nomango
 | ||||
| //
 | ||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy
 | ||||
| // of this software and associated documentation files (the "Software"), to deal
 | ||||
| // in the Software without restriction, including without limitation the rights
 | ||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | ||||
| // copies of the Software, and to permit persons to whom the Software is
 | ||||
| // furnished to do so, subject to the following conditions:
 | ||||
| //
 | ||||
| // The above copyright notice and this permission notice shall be included in
 | ||||
| // all copies or substantial portions of the Software.
 | ||||
| //
 | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | ||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | ||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | ||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | ||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | ||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | ||||
| // THE SOFTWARE.
 | ||||
| 
 | ||||
| #pragma once | ||||
| #include <kiwano/core/Common.h> | ||||
| #include <kiwano/base/ObjectBase.h> | ||||
| 
 | ||||
| namespace kiwano | ||||
| { | ||||
| 
 | ||||
| KGE_DECLARE_SMART_PTR(ConfigIni); | ||||
| 
 | ||||
| /// \~chinese
 | ||||
| /// @brief ini格式文件
 | ||||
| class KGE_API ConfigIni : public ObjectBase | ||||
| { | ||||
| public: | ||||
|     /// \~chinese
 | ||||
|     /// @brief 加载 ini 文件
 | ||||
|     /// @param file_path 文件路径
 | ||||
|     static ConfigIniPtr Create(const String& file_path); | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 键值字典
 | ||||
|     typedef Map<String, String> ValueMap; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief Section字典
 | ||||
|     typedef UnorderedMap<String, ValueMap> SectionMap; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 加载 ini 文件
 | ||||
|     /// @param file_path 文件路径
 | ||||
|     bool Load(const String& file_path); | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 加载 ini 文件
 | ||||
|     /// @param is 输入流
 | ||||
|     bool Load(std::istream& is); | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 保存至 ini 文件
 | ||||
|     /// @param file_path 文件路径
 | ||||
|     bool Save(const String& file_path); | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 保存至 ini 文件
 | ||||
|     /// @param os 输出流
 | ||||
|     bool Save(std::ostream& os); | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 获取所有section
 | ||||
|     SectionMap GetSectionMap() const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 获取section
 | ||||
|     /// @param section section的名称
 | ||||
|     ValueMap GetSection(const String& section) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 获取值
 | ||||
|     /// @param section section的名称
 | ||||
|     /// @param key key的名称
 | ||||
|     String GetString(const String& section, const String& key) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 获取值
 | ||||
|     /// @param section section的名称
 | ||||
|     /// @param key key的名称
 | ||||
|     /// @param default_value 不存在时的默认值
 | ||||
|     float GetFloat(const String& section, const String& key, float default_value = 0.0f) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 获取值
 | ||||
|     /// @param section section的名称
 | ||||
|     /// @param key key的名称
 | ||||
|     /// @param default_value 不存在时的默认值
 | ||||
|     double GetDouble(const String& section, const String& key, double default_value = 0.0) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 获取值
 | ||||
|     /// @param section section的名称
 | ||||
|     /// @param key key的名称
 | ||||
|     /// @param default_value 不存在时的默认值
 | ||||
|     int GetInt(const String& section, const String& key, int default_value = 0) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 获取值
 | ||||
|     /// @param section section的名称
 | ||||
|     /// @param key key的名称
 | ||||
|     /// @param default_value 不存在时的默认值
 | ||||
|     bool GetBool(const String& section, const String& key, bool default_value = false) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 是否存在section
 | ||||
|     /// @param section section的名称
 | ||||
|     bool HasSection(const String& section) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 是否存在值
 | ||||
|     /// @param section section的名称
 | ||||
|     /// @param key key的名称
 | ||||
|     bool HasValue(const String& section, const String& key) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 设置所有section
 | ||||
|     /// @param sections section字典
 | ||||
|     void SetSectionMap(const SectionMap& sections); | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 设置section
 | ||||
|     /// @param section section的名称
 | ||||
|     /// @param values 键值字典
 | ||||
|     void SetSection(const String& section, const ValueMap& values); | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 设置值
 | ||||
|     /// @param section section的名称
 | ||||
|     /// @param key key的名称
 | ||||
|     /// @param value 值
 | ||||
|     void SetString(const String& section, const String& key, const String& value); | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 设置值
 | ||||
|     /// @param section section的名称
 | ||||
|     /// @param key key的名称
 | ||||
|     /// @param value 值
 | ||||
|     void SetFloat(const String& section, const String& key, float value); | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 设置值
 | ||||
|     /// @param section section的名称
 | ||||
|     /// @param key key的名称
 | ||||
|     /// @param value 值
 | ||||
|     void SetDouble(const String& section, const String& key, double value); | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 设置值
 | ||||
|     /// @param section section的名称
 | ||||
|     /// @param key key的名称
 | ||||
|     /// @param value 值
 | ||||
|     void SetInt(const String& section, const String& key, int value); | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 设置值
 | ||||
|     /// @param section section的名称
 | ||||
|     /// @param key key的名称
 | ||||
|     /// @param value 值
 | ||||
|     void SetBool(const String& section, const String& key, bool value); | ||||
| 
 | ||||
|     ValueMap& operator[](const String& section); | ||||
| 
 | ||||
|     const ValueMap& operator[](const String& section) const; | ||||
| 
 | ||||
| private: | ||||
|     void ParseLine(StringView line, String* section); | ||||
| 
 | ||||
| private: | ||||
|     SectionMap sections_; | ||||
| }; | ||||
| 
 | ||||
| }  // namespace kiwano
 | ||||
|  | @ -1,106 +0,0 @@ | |||
| // Copyright (c) 2016-2018 Kiwano - Nomango
 | ||||
| //
 | ||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy
 | ||||
| // of this software and associated documentation files (the "Software"), to deal
 | ||||
| // in the Software without restriction, including without limitation the rights
 | ||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | ||||
| // copies of the Software, and to permit persons to whom the Software is
 | ||||
| // furnished to do so, subject to the following conditions:
 | ||||
| //
 | ||||
| // The above copyright notice and this permission notice shall be included in
 | ||||
| // all copies or substantial portions of the Software.
 | ||||
| //
 | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | ||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | ||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | ||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | ||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | ||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | ||||
| // THE SOFTWARE.
 | ||||
| 
 | ||||
| #include <kiwano/utils/LocalStorage.h> | ||||
| 
 | ||||
| namespace kiwano | ||||
| { | ||||
| 
 | ||||
| LocalStorage::LocalStorage(const String& file_path, const String& field) | ||||
| { | ||||
|     SetFilePath(file_path); | ||||
|     SetFieldName(field); | ||||
| } | ||||
| 
 | ||||
| bool LocalStorage::Exists(const String& key) const | ||||
| { | ||||
|     char temp[256] = { 0 }; | ||||
|     ::GetPrivateProfileStringA(field_name_.c_str(), key.c_str(), "", temp, 255, file_path_.c_str()); | ||||
|     return temp[0] == '\0'; | ||||
| } | ||||
| 
 | ||||
| bool LocalStorage::SaveInt(const String& key, int val) const | ||||
| { | ||||
|     BOOL ret = | ||||
|         ::WritePrivateProfileStringA(field_name_.c_str(), key.c_str(), std::to_string(val).c_str(), file_path_.c_str()); | ||||
|     return ret == TRUE; | ||||
| } | ||||
| 
 | ||||
| bool LocalStorage::SaveFloat(const String& key, float val) const | ||||
| { | ||||
|     BOOL ret = | ||||
|         ::WritePrivateProfileStringA(field_name_.c_str(), key.c_str(), std::to_string(val).c_str(), file_path_.c_str()); | ||||
|     return ret == TRUE; | ||||
| } | ||||
| 
 | ||||
| bool LocalStorage::SaveDouble(const String& key, double val) const | ||||
| { | ||||
|     BOOL ret = | ||||
|         ::WritePrivateProfileStringA(field_name_.c_str(), key.c_str(), std::to_string(val).c_str(), file_path_.c_str()); | ||||
|     return ret == TRUE; | ||||
| } | ||||
| 
 | ||||
| bool LocalStorage::SaveBool(const String& key, bool val) const | ||||
| { | ||||
|     BOOL ret = ::WritePrivateProfileStringA(field_name_.c_str(), key.c_str(), (val ? "1" : "0"), file_path_.c_str()); | ||||
|     return ret == TRUE; | ||||
| } | ||||
| 
 | ||||
| bool LocalStorage::SaveString(const String& key, const String& val) const | ||||
| { | ||||
|     BOOL ret = ::WritePrivateProfileStringA(field_name_.c_str(), key.c_str(), val.c_str(), file_path_.c_str()); | ||||
|     return ret == TRUE; | ||||
| } | ||||
| 
 | ||||
| int LocalStorage::GetInt(const String& key, int default_value) const | ||||
| { | ||||
|     return ::GetPrivateProfileIntA(field_name_.c_str(), key.c_str(), default_value, file_path_.c_str()); | ||||
| } | ||||
| 
 | ||||
| float LocalStorage::GetFloat(const String& key, float default_value) const | ||||
| { | ||||
|     char   temp[32]    = { 0 }; | ||||
|     String default_str = std::to_string(default_value); | ||||
|     ::GetPrivateProfileStringA(field_name_.c_str(), key.c_str(), default_str.c_str(), temp, 31, file_path_.c_str()); | ||||
|     return std::stof(temp); | ||||
| } | ||||
| 
 | ||||
| double LocalStorage::GetDouble(const String& key, double default_value) const | ||||
| { | ||||
|     char   temp[32]    = { 0 }; | ||||
|     String default_str = std::to_string(default_value); | ||||
|     ::GetPrivateProfileStringA(field_name_.c_str(), key.c_str(), default_str.c_str(), temp, 31, file_path_.c_str()); | ||||
|     return std::stod(temp); | ||||
| } | ||||
| 
 | ||||
| bool LocalStorage::GetBool(const String& key, bool default_value) const | ||||
| { | ||||
|     int nValue = ::GetPrivateProfileIntA(field_name_.c_str(), key.c_str(), default_value ? 1 : 0, file_path_.c_str()); | ||||
|     return nValue == TRUE; | ||||
| } | ||||
| 
 | ||||
| String LocalStorage::GetString(const String& key, const String& default_value) const | ||||
| { | ||||
|     char temp[256] = { 0 }; | ||||
|     ::GetPrivateProfileStringA(field_name_.c_str(), key.c_str(), default_value.c_str(), temp, 255, file_path_.c_str()); | ||||
|     return temp; | ||||
| } | ||||
| 
 | ||||
| }  // namespace kiwano
 | ||||
|  | @ -1,163 +0,0 @@ | |||
| // Copyright (c) 2016-2018 Kiwano - Nomango
 | ||||
| //
 | ||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy
 | ||||
| // of this software and associated documentation files (the "Software"), to deal
 | ||||
| // in the Software without restriction, including without limitation the rights
 | ||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | ||||
| // copies of the Software, and to permit persons to whom the Software is
 | ||||
| // furnished to do so, subject to the following conditions:
 | ||||
| //
 | ||||
| // The above copyright notice and this permission notice shall be included in
 | ||||
| // all copies or substantial portions of the Software.
 | ||||
| //
 | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | ||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | ||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | ||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | ||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | ||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | ||||
| // THE SOFTWARE.
 | ||||
| 
 | ||||
| #pragma once | ||||
| #include <kiwano/core/Common.h> | ||||
| #include <kiwano/base/ObjectBase.h> | ||||
| 
 | ||||
| namespace kiwano | ||||
| { | ||||
| 
 | ||||
| KGE_DECLARE_SMART_PTR(LocalStorage); | ||||
| 
 | ||||
| /// \~chinese
 | ||||
| /// @brief 本地存储
 | ||||
| /// @details LocalStorage是一个简易的持久化工具,存放(字符串-值)的键值对
 | ||||
| /// 支持的数据类型包括 (bool | int | float | double | String)
 | ||||
| /// 例如, 保存游戏最高分, 以便下次进行游戏时读取:
 | ||||
| ///   @code
 | ||||
| ///     LocalStorage data;                      // 创建数据对象
 | ||||
| ///     data.SaveInt("best-score", 20);        // 保存最高分 20
 | ||||
| ///     int best = data.GetInt("best-score");  // 读取之前储存的最高分
 | ||||
| ///   @endcode
 | ||||
| class KGE_API LocalStorage : public ObjectBase | ||||
| { | ||||
| public: | ||||
|     /// \~chinese
 | ||||
|     /// @brief 构建本地存储对象
 | ||||
|     /// @param file_path 文件储存路径
 | ||||
|     /// @param field 字段名
 | ||||
|     LocalStorage(const String& file_path = "data.ini", const String& field = "defalut"); | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 获取文件储存路径
 | ||||
|     const String& GetFilePath() const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 设置文件储存路径
 | ||||
|     void SetFilePath(const String& file_path); | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 获取字段名
 | ||||
|     const String& GetFieldName() const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 设置字段名
 | ||||
|     void SetFieldName(const String& field); | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 判断键对应的数据是否存在
 | ||||
|     bool Exists(const String& key) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 保存 int 类型的值
 | ||||
|     /// @param key 键
 | ||||
|     /// @param val 值
 | ||||
|     /// @return 操作是否成功
 | ||||
|     bool SaveInt(const String& key, int val) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 保存 float 类型的值
 | ||||
|     /// @param key 键
 | ||||
|     /// @param val 值
 | ||||
|     /// @return 操作是否成功
 | ||||
|     bool SaveFloat(const String& key, float val) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 保存 double 类型的值
 | ||||
|     /// @param key 键
 | ||||
|     /// @param val 值
 | ||||
|     /// @return 操作是否成功
 | ||||
|     bool SaveDouble(const String& key, double val) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 保存 bool 类型的值
 | ||||
|     /// @param key 键
 | ||||
|     /// @param val 值
 | ||||
|     /// @return 操作是否成功
 | ||||
|     bool SaveBool(const String& key, bool val) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 保存 String 类型的值
 | ||||
|     /// @param key 键
 | ||||
|     /// @param val 值
 | ||||
|     /// @return 操作是否成功
 | ||||
|     bool SaveString(const String& key, const String& val) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 获取 int 类型的值
 | ||||
|     /// @param key 键
 | ||||
|     /// @param default_value 值不存在时返回的默认值
 | ||||
|     /// @return 值
 | ||||
|     int GetInt(const String& key, int default_value = 0) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 获取 float 类型的值
 | ||||
|     /// @param key 键
 | ||||
|     /// @param default_value 值不存在时返回的默认值
 | ||||
|     /// @return 值
 | ||||
|     float GetFloat(const String& key, float default_value = 0.0f) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 获取 double 类型的值
 | ||||
|     /// @param key 键
 | ||||
|     /// @param default_value 值不存在时返回的默认值
 | ||||
|     /// @return 值
 | ||||
|     double GetDouble(const String& key, double default_value = 0.0) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 获取 bool 类型的值
 | ||||
|     /// @param key 键
 | ||||
|     /// @param default_value 值不存在时返回的默认值
 | ||||
|     /// @return 值
 | ||||
|     bool GetBool(const String& key, bool default_value = false) const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 获取 字符串 类型的值
 | ||||
|     /// @param key 键
 | ||||
|     /// @param default_value 值不存在时返回的默认值
 | ||||
|     /// @return 值
 | ||||
|     String GetString(const String& key, const String& default_value = String()) const; | ||||
| 
 | ||||
| private: | ||||
|     String file_path_; | ||||
|     String field_name_; | ||||
| }; | ||||
| 
 | ||||
| inline const String& LocalStorage::GetFilePath() const | ||||
| { | ||||
|     return file_path_; | ||||
| } | ||||
| 
 | ||||
| inline const String& LocalStorage::GetFieldName() const | ||||
| { | ||||
|     return field_name_; | ||||
| } | ||||
| 
 | ||||
| inline void LocalStorage::SetFilePath(const String& file_path) | ||||
| { | ||||
|     file_path_ = file_path; | ||||
| } | ||||
| 
 | ||||
| inline void LocalStorage::SetFieldName(const String& field_name) | ||||
| { | ||||
|     field_name_ = field_name; | ||||
| } | ||||
| }  // namespace kiwano
 | ||||
|  | @ -65,9 +65,16 @@ bool Ticker::Tick(Duration dt) | |||
|     if (ticked_count_ == total_tick_count_) | ||||
|         return false; | ||||
| 
 | ||||
|     if (interval_.IsZero()) | ||||
|     { | ||||
|         delta_time_ = dt; | ||||
|         ++ticked_count_; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     elapsed_time_ += dt; | ||||
| 
 | ||||
|     if (elapsed_time_ + error_time_ > interval_) | ||||
|     if (elapsed_time_ + error_time_ >= interval_) | ||||
|     { | ||||
|         delta_time_   = elapsed_time_; | ||||
|         error_time_   = (elapsed_time_ + error_time_) - interval_; | ||||
|  |  | |||
|  | @ -87,6 +87,10 @@ public: | |||
|     /// @brief 设置报时间隔
 | ||||
|     void SetInterval(Duration interval); | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 获取时间误差
 | ||||
|     Duration GetErrorTime() const; | ||||
| 
 | ||||
|     /// \~chinese
 | ||||
|     /// @brief 获取计时器
 | ||||
|     TimerPtr GetTimer(); | ||||
|  | @ -140,4 +144,9 @@ inline void Ticker::SetInterval(Duration interval) | |||
|     interval_ = interval; | ||||
| } | ||||
| 
 | ||||
| inline Duration Ticker::GetErrorTime() const | ||||
| { | ||||
|     return error_time_; | ||||
| } | ||||
| 
 | ||||
| }  // namespace kiwano
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue