From de4b480dc2ff86e88edcf33edf13dd70741d91d0 Mon Sep 17 00:00:00 2001 From: Nomango Date: Wed, 27 May 2020 16:38:58 +0800 Subject: [PATCH 1/5] update Application frame ticker --- src/kiwano/core/Time.h | 6 ++-- src/kiwano/platform/Application.cpp | 37 ++++++++++++++-------- src/kiwano/platform/Application.h | 39 +++++++++++++++--------- src/kiwano/platform/win32/WindowImpl.cpp | 6 ++++ src/kiwano/utils/Ticker.cpp | 9 +++++- src/kiwano/utils/Ticker.h | 9 ++++++ 6 files changed, 75 insertions(+), 31 deletions(-) diff --git a/src/kiwano/core/Time.h b/src/kiwano/core/Time.h index 1cf09285..10b0ce37 100644 --- a/src/kiwano/core/Time.h +++ b/src/kiwano/core/Time.h @@ -263,17 +263,17 @@ inline void Duration::SetMilliseconds(int64_t ms) inline void Duration::SetSeconds(float seconds) { - milliseconds_ = static_cast(seconds * 1000.f); + milliseconds_ = static_cast(seconds * 1000.f); } inline void Duration::SetMinutes(float minutes) { - milliseconds_ = static_cast(minutes * 60 * 1000.f); + milliseconds_ = static_cast(minutes * 60 * 1000.f); } inline void Duration::SetHours(float hours) { - milliseconds_ = static_cast(hours * 60 * 60 * 1000.f); + milliseconds_ = static_cast(hours * 60 * 60 * 1000.f); } inline bool Time::IsZero() const diff --git a/src/kiwano/platform/Application.cpp b/src/kiwano/platform/Application.cpp index 52a1533f..3aeec82c 100644 --- a/src/kiwano/platform/Application.cpp +++ b/src/kiwano/platform/Application.cpp @@ -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() diff --git a/src/kiwano/platform/Application.h b/src/kiwano/platform/Application.h index 10765c55..f893e2bb 100644 --- a/src/kiwano/platform/Application.h +++ b/src/kiwano/platform/Application.h @@ -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 modules_; std::mutex perform_mutex_; Queue> 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 diff --git a/src/kiwano/platform/win32/WindowImpl.cpp b/src/kiwano/platform/win32/WindowImpl.cpp index be5ae065..1aba29ef 100644 --- a/src/kiwano/platform/win32/WindowImpl.cpp +++ b/src/kiwano/platform/win32/WindowImpl.cpp @@ -34,6 +34,8 @@ #include // GET_X_LPARAM, GET_Y_LPARAM #include // ImmAssociateContext #pragma comment(lib, "imm32.lib") +#include // 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) diff --git a/src/kiwano/utils/Ticker.cpp b/src/kiwano/utils/Ticker.cpp index d44dc5ac..bbb40fdd 100644 --- a/src/kiwano/utils/Ticker.cpp +++ b/src/kiwano/utils/Ticker.cpp @@ -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_; diff --git a/src/kiwano/utils/Ticker.h b/src/kiwano/utils/Ticker.h index 815bfe8c..09d9df2b 100644 --- a/src/kiwano/utils/Ticker.h +++ b/src/kiwano/utils/Ticker.h @@ -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 From cd3a7e21339d6576965efba2a6d933085e13b49d Mon Sep 17 00:00:00 2001 From: Nomango Date: Thu, 28 May 2020 02:41:59 +0800 Subject: [PATCH 2/5] add StringView --- src/kiwano/core/String.h | 358 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 356 insertions(+), 2 deletions(-) diff --git a/src/kiwano/core/String.h b/src/kiwano/core/String.h index cdb45b54..2307a5f7 100644 --- a/src/kiwano/core/String.h +++ b/src/kiwano/core/String.h @@ -20,17 +20,24 @@ #pragma once #include +#include namespace kiwano { +/// \~chinese +/// @brief 基础字符串容器 +template +using BasicString = std::basic_string; + /// \~chinese /// @brief 字符串容器 -using String = std::string; +using String = BasicString; /// \~chinese /// @brief 宽字符串容器 -using WideString = std::wstring; +using WideString = BasicString; + namespace strings { @@ -53,4 +60,351 @@ WideString NarrowToWide(const String& str); } + +/// \~chinese +/// @brief 基础常字符串视图 +template +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; + using size_type = std::size_t; + using string_type = BasicString; + + 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_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(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; + 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; + +/// \~chinese +/// @brief 宽字符串视图 +using WideStringView = BasicStringView; + } // namespace kiwano From e7da6a5abcbda7ebd9130a6ed3d712614335abc3 Mon Sep 17 00:00:00 2001 From: Nomango Date: Thu, 28 May 2020 02:42:32 +0800 Subject: [PATCH 3/5] add ConfigIni & remove LocalStorage --- projects/kiwano/kiwano.vcxproj | 4 +- projects/kiwano/kiwano.vcxproj.filters | 12 +- src/kiwano/kiwano.h | 2 +- src/kiwano/utils/ConfigIni.cpp | 353 +++++++++++++++++++++++++ src/kiwano/utils/ConfigIni.h | 185 +++++++++++++ src/kiwano/utils/LocalStorage.cpp | 106 -------- src/kiwano/utils/LocalStorage.h | 163 ------------ 7 files changed, 547 insertions(+), 278 deletions(-) create mode 100644 src/kiwano/utils/ConfigIni.cpp create mode 100644 src/kiwano/utils/ConfigIni.h delete mode 100644 src/kiwano/utils/LocalStorage.cpp delete mode 100644 src/kiwano/utils/LocalStorage.h diff --git a/projects/kiwano/kiwano.vcxproj b/projects/kiwano/kiwano.vcxproj index 06fa487e..8883793a 100644 --- a/projects/kiwano/kiwano.vcxproj +++ b/projects/kiwano/kiwano.vcxproj @@ -96,9 +96,9 @@ + - @@ -174,8 +174,8 @@ + - diff --git a/projects/kiwano/kiwano.vcxproj.filters b/projects/kiwano/kiwano.vcxproj.filters index 9a950f94..9d5619f2 100644 --- a/projects/kiwano/kiwano.vcxproj.filters +++ b/projects/kiwano/kiwano.vcxproj.filters @@ -105,9 +105,6 @@ 2d\action - - utils - utils @@ -351,6 +348,9 @@ base\component + + utils + @@ -401,9 +401,6 @@ 2d\action - - utils - utils @@ -575,6 +572,9 @@ base\component + + utils + diff --git a/src/kiwano/kiwano.h b/src/kiwano/kiwano.h index 50ef674e..1bd37f0f 100644 --- a/src/kiwano/kiwano.h +++ b/src/kiwano/kiwano.h @@ -127,7 +127,6 @@ // #include -#include #include #include #include @@ -135,3 +134,4 @@ #include #include #include +#include diff --git a/src/kiwano/utils/ConfigIni.cpp b/src/kiwano/utils/ConfigIni.cpp new file mode 100644 index 00000000..ed2374e5 --- /dev/null +++ b/src/kiwano/utils/ConfigIni.cpp @@ -0,0 +1,353 @@ +// 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 +#include +#include +#include + +#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(); + 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 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") + return true; + else if (str == "false") + 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"); +} + +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 diff --git a/src/kiwano/utils/ConfigIni.h b/src/kiwano/utils/ConfigIni.h new file mode 100644 index 00000000..dac12b6c --- /dev/null +++ b/src/kiwano/utils/ConfigIni.h @@ -0,0 +1,185 @@ +// 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 +#include + +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 ValueMap; + + /// \~chinese + /// @brief Section字典 + typedef UnorderedMap 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); + + inline ValueMap& operator[](const String& section) + { + return sections_[section]; + } + + inline const ValueMap& operator[](const String& section) const + { + return sections_.at(section); + } + +private: + void ParseLine(StringView line, String* section); + +private: + SectionMap sections_; +}; + +} // namespace kiwano diff --git a/src/kiwano/utils/LocalStorage.cpp b/src/kiwano/utils/LocalStorage.cpp deleted file mode 100644 index 6e98d9ab..00000000 --- a/src/kiwano/utils/LocalStorage.cpp +++ /dev/null @@ -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 - -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 diff --git a/src/kiwano/utils/LocalStorage.h b/src/kiwano/utils/LocalStorage.h deleted file mode 100644 index d3475cab..00000000 --- a/src/kiwano/utils/LocalStorage.h +++ /dev/null @@ -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 -#include - -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 From f5c9e5e226fe6304a863e36f398a96a645d15a6d Mon Sep 17 00:00:00 2001 From: Nomango Date: Thu, 28 May 2020 02:51:01 +0800 Subject: [PATCH 4/5] fix header --- src/kiwano/utils/ConfigIni.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/kiwano/utils/ConfigIni.cpp b/src/kiwano/utils/ConfigIni.cpp index ed2374e5..8af2a499 100644 --- a/src/kiwano/utils/ConfigIni.cpp +++ b/src/kiwano/utils/ConfigIni.cpp @@ -20,8 +20,9 @@ #include #include -#include -#include +#include // std::ifstream, std::ofstream +#include // std::sort, std::for_each +#include // std::isspace #define KGE_DEFAULT_INI_SECTION_NAME "default" From bd8b328d019ea6e26787396871a3f738b7f818d9 Mon Sep 17 00:00:00 2001 From: Nomango Date: Thu, 28 May 2020 11:40:51 +0800 Subject: [PATCH 5/5] update ConfigIni --- src/kiwano/utils/ConfigIni.cpp | 22 ++++++++++++++++++++-- src/kiwano/utils/ConfigIni.h | 10 ++-------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/kiwano/utils/ConfigIni.cpp b/src/kiwano/utils/ConfigIni.cpp index 8af2a499..1b70c758 100644 --- a/src/kiwano/utils/ConfigIni.cpp +++ b/src/kiwano/utils/ConfigIni.cpp @@ -261,9 +261,9 @@ bool ConfigIni::GetBool(const String& section, const String& key, bool default_v String str = GetString(section, key); if (!str.empty()) { - if (str == "true") + if (str == "true" || str == "1") return true; - else if (str == "false") + else if (str == "false" || str == "0") return false; } return default_value; @@ -324,6 +324,24 @@ 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(sections_).insert(std::make_pair(section, ValueMap())); + } + return sections_.at(section); +} + void ConfigIni::ParseLine(StringView line, String* section) { line = Trim(line); diff --git a/src/kiwano/utils/ConfigIni.h b/src/kiwano/utils/ConfigIni.h index dac12b6c..e83434f2 100644 --- a/src/kiwano/utils/ConfigIni.h +++ b/src/kiwano/utils/ConfigIni.h @@ -165,15 +165,9 @@ public: /// @param value 值 void SetBool(const String& section, const String& key, bool value); - inline ValueMap& operator[](const String& section) - { - return sections_[section]; - } + ValueMap& operator[](const String& section); - inline const ValueMap& operator[](const String& section) const - { - return sections_.at(section); - } + const ValueMap& operator[](const String& section) const; private: void ParseLine(StringView line, String* section);