diff --git a/src/RA_Defs.h b/src/RA_Defs.h
index 0cbdc228..a4192776 100644
--- a/src/RA_Defs.h
+++ b/src/RA_Defs.h
@@ -27,7 +27,6 @@
#else
#include "util\Log.hh"
-#include "RA_Json.h"
// RA-Only
using namespace std::string_literals;
diff --git a/src/RA_Integration.vcxproj b/src/RA_Integration.vcxproj
index fbdecff4..ccf61870 100644
--- a/src/RA_Integration.vcxproj
+++ b/src/RA_Integration.vcxproj
@@ -84,7 +84,6 @@
-
diff --git a/src/RA_Integration.vcxproj.filters b/src/RA_Integration.vcxproj.filters
index 66152c94..3dc3891c 100644
--- a/src/RA_Integration.vcxproj.filters
+++ b/src/RA_Integration.vcxproj.filters
@@ -87,9 +87,6 @@
UI\Win32
-
- Services
-
UI\ViewModels
diff --git a/src/RA_Json.cpp b/src/RA_Json.cpp
deleted file mode 100644
index c4d6f638..00000000
--- a/src/RA_Json.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-#include "RA_Json.h"
-
-#include "services\impl\FileTextReader.hh"
-#include "services\impl\FileTextWriter.hh"
-#include "services\impl\StringTextReader.hh"
-#include "services\impl\StringTextWriter.hh"
-
-_Use_decl_annotations_
-bool LoadDocument(rapidjson::Document& doc, ra::services::TextReader& reader)
-{
- auto* pFileTextReader = dynamic_cast(&reader);
- if (pFileTextReader != nullptr)
- {
- auto& iFile = pFileTextReader->GetFStream();
- if (!iFile.is_open())
- return false;
-
- rapidjson::IStreamWrapper iStreamWrapper(iFile);
- doc.ParseStream(iStreamWrapper);
- }
- else
- {
- auto* pStringTextReader = dynamic_cast(&reader);
- if (pStringTextReader != nullptr)
- {
- doc.Parse(pStringTextReader->GetString());
- }
- else
- {
- assert(!"Unsupported TextReader");
- return false;
- }
- }
-
- return !doc.HasParseError();
-}
-
-bool SaveDocument(_In_ const rapidjson::Document& doc, ra::services::TextWriter& writer)
-{
- auto* pFileTextWriter = dynamic_cast(&writer);
- if (pFileTextWriter != nullptr)
- {
- auto& oFile = pFileTextWriter->GetFStream();
- if (!oFile.is_open())
- return false;
-
- rapidjson::OStreamWrapper oStreamWrapper(oFile);
- rapidjson::Writer oStreamWriter(oStreamWrapper);
- return doc.Accept(oStreamWriter);
- }
-
- auto* pStringTextWriter = dynamic_cast(&writer);
- if (pStringTextWriter != nullptr)
- {
- rapidjson::StringBuffer oStringBuffer;
- rapidjson::Writer oStreamWriter(oStringBuffer);
- if (!doc.Accept(oStreamWriter))
- return false;
-
- pStringTextWriter->GetString().assign(oStringBuffer.GetString());
- return true;
- }
-
- assert(!"Unsupported TextWriter");
- return false;
-}
diff --git a/src/RA_Json.h b/src/RA_Json.h
deleted file mode 100644
index fab8c279..00000000
--- a/src/RA_Json.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef RA_JSON_H
-#define RA_JSON_H
-#pragma once
-
-#include "services\TextReader.hh"
-#include "services\TextWriter.hh"
-
-_Success_(return)
-_NODISCARD bool LoadDocument(_Out_ rapidjson::Document& doc, _In_ ra::services::TextReader& reader);
-bool SaveDocument(_In_ const rapidjson::Document& doc, ra::services::TextWriter& writer);
-
-#endif // !RA_JSON_H
diff --git a/src/api/impl/ConnectedServer.cpp b/src/api/impl/ConnectedServer.cpp
index 96b1c309..0a0ec043 100644
--- a/src/api/impl/ConnectedServer.cpp
+++ b/src/api/impl/ConnectedServer.cpp
@@ -1,28 +1,19 @@
#include "ConnectedServer.hh"
-#include "DisconnectedServer.hh"
-#include "RA_Defs.h"
+#include "util\Json.hh"
#include "util\Log.hh"
-#include "RA_md5factory.h"
-
#include "context\UserContext.hh"
#include "services\Http.hh"
#include "services\IFileSystem.hh"
#include "services\IHttpRequester.hh"
-#include "services\ILocalStorage.hh"
#include "services\ServiceLocator.hh"
-#include
-
-#include
-
#include // for parsing cached patchdata response
#include
#include
#include
-#include
namespace ra {
namespace api {
@@ -56,12 +47,10 @@ _NODISCARD static bool HandleHttpError(_In_ const ra::services::Http::StatusCode
_NODISCARD static bool GetJson([[maybe_unused]] _In_ const char* sApiName,
_In_ const ra::services::Http::Response& httpResponse,
- _Inout_ ApiResponseBase& pResponse, _Out_ rapidjson::Document& pDocument)
+ _Inout_ ApiResponseBase& pResponse, _Inout_ ra::util::Json::Reader& pJson)
{
if (httpResponse.Content().empty())
{
- pDocument.SetArray();
-
if (!HandleHttpError(httpResponse.StatusCode(), pResponse))
{
pResponse.ErrorMessage = "Empty JSON response";
@@ -74,8 +63,7 @@ _NODISCARD static bool GetJson([[maybe_unused]] _In_ const char* sApiName,
RA_LOG_INFO("-- %s Response: %s", sApiName, httpResponse.Content());
- pDocument.Parse(httpResponse.Content());
- if (pDocument.HasParseError())
+ if (!pJson.Parse(httpResponse.Content()))
{
if (HandleHttpError(httpResponse.StatusCode(), pResponse))
{
@@ -87,7 +75,7 @@ _NODISCARD static bool GetJson([[maybe_unused]] _In_ const char* sApiName,
pResponse.Result = ApiResult::Error;
- if (pDocument.GetParseError() == rapidjson::kParseErrorValueInvalid && pDocument.GetErrorOffset() == 0)
+ if (pJson.GetParseErrorOffset() == 0)
{
// server did not return JSON, check for HTML
if (ra::util::String::StartsWith(httpResponse.Content(), ""))
@@ -123,15 +111,15 @@ _NODISCARD static bool GetJson([[maybe_unused]] _In_ const char* sApiName,
if (pResponse.ErrorMessage.empty())
{
pResponse.ErrorMessage =
- ra::util::String::Printf("JSON Parse Error: %s (at %zu)", GetParseError_En(pDocument.GetParseError()), pDocument.GetErrorOffset());
+ ra::util::String::Printf("JSON Parse Error: %s (at %zu)", pJson.GetParseError(), pJson.GetParseErrorOffset());
}
return false;
}
- if (pDocument.HasMember("Error"))
+ std::string sError;
+ if (pJson.TryGetString("Error", pResponse.ErrorMessage))
{
- pResponse.ErrorMessage = pDocument["Error"].GetString();
if (httpResponse.StatusCode() == ra::services::Http::StatusCode::TooManyRequests)
{
pResponse.Result = ApiResult::Incomplete;
@@ -146,7 +134,7 @@ _NODISCARD static bool GetJson([[maybe_unused]] _In_ const char* sApiName,
}
}
- if (pDocument.HasMember("Success") && !pDocument["Success"].GetBool())
+ if (pJson.GetBoolean("Success", true) == false)
{
pResponse.Result = ApiResult::Failed;
RA_LOG_ERR("-- %s Error: Success=false", sApiName);
@@ -162,27 +150,6 @@ _NODISCARD static bool GetJson([[maybe_unused]] _In_ const char* sApiName,
return true;
}
-static void GetRequiredJsonField(_Out_ std::string& sValue, _In_ const rapidjson::Value& pDocument,
- _In_ const char* const sField, _Inout_ ApiResponseBase& response)
-{
- if (!pDocument.HasMember(sField))
- {
- sValue.clear();
-
- response.Result = ApiResult::Error;
- if (response.ErrorMessage.empty())
- response.ErrorMessage = ra::util::String::Printf("%s not found in response", sField);
- }
- else
- {
- auto& pField = pDocument[sField];
- if (pField.IsString())
- sValue = pField.GetString();
- else
- sValue.clear();
- }
-}
-
static bool DoRequestWithoutLog(const rc_api_request_t& api_request, _UNUSED const char* sApiName, ra::services::Http::Response& pHttpResponse, ApiResponseBase& pResponse)
{
ra::services::Http::Request httpRequest(api_request.url);
@@ -326,7 +293,7 @@ static bool ValidateResponse(int nResult, const rc_api_response_t& api_response,
}
static bool DoUpload(const std::string& sHost, const char* _RESTRICT sApiName, const char* _RESTRICT sRequestName,
- const std::wstring& sFilePath, ApiResponseBase& pResponse, rapidjson::Document& document)
+ const std::wstring& sFilePath, ApiResponseBase& pResponse, ra::util::Json::Reader& pJson)
{
const auto& pFileSystem = ra::services::ServiceLocator::Get();
const auto nFileSize = pFileSystem.GetFileSize(sFilePath);
@@ -391,7 +358,7 @@ static bool DoUpload(const std::string& sHost, const char* _RESTRICT sApiName, c
httpRequest.SetPostData(sPostData);
const auto httpResponse = httpRequest.Call();
- return GetJson(sApiName, httpResponse, pResponse, document);
+ return GetJson(sApiName, httpResponse, pResponse, pJson);
}
// === APIs ===
@@ -728,12 +695,13 @@ UpdateRichPresence::Response ConnectedServer::UpdateRichPresence(const UpdateRic
UploadBadge::Response ConnectedServer::UploadBadge(const UploadBadge::Request& request)
{
UploadBadge::Response response;
- rapidjson::Document document;
+ ra::util::Json::Reader pJson;
std::string sPostData;
- if (DoUpload(m_sHost, UploadBadge::Name(), "uploadbadgeimage", request.ImageFilePath, response, document))
+ if (DoUpload(m_sHost, UploadBadge::Name(), "uploadbadgeimage", request.ImageFilePath, response, pJson))
{
- if (!document.HasMember("Response"))
+ ra::util::Json::Reader::Node pResponse;
+ if (!pJson.TryGetObject("Response", pResponse))
{
if (response.Result == ApiResult::None)
response.Result = ApiResult::Error;
@@ -744,7 +712,13 @@ UploadBadge::Response ConnectedServer::UploadBadge(const UploadBadge::Request& r
else
{
response.Result = ApiResult::Success;
- GetRequiredJsonField(response.BadgeId, document["Response"], "BadgeIter", response);
+
+ if (!pResponse.TryGetString("BadgeIter", response.BadgeId))
+ {
+ response.Result = ApiResult::Error;
+ if (response.ErrorMessage.empty())
+ response.ErrorMessage = ra::util::String::Printf("%s not found in response", "BadgeIter");
+ }
}
}
diff --git a/src/devkit/RADevKit.vcxproj b/src/devkit/RADevKit.vcxproj
index e9d7ba5d..5a531e75 100644
--- a/src/devkit/RADevKit.vcxproj
+++ b/src/devkit/RADevKit.vcxproj
@@ -84,6 +84,7 @@
+
@@ -144,6 +145,7 @@
+
diff --git a/src/devkit/RADevKit.vcxproj.filters b/src/devkit/RADevKit.vcxproj.filters
index 8caaac06..8b38dcef 100644
--- a/src/devkit/RADevKit.vcxproj.filters
+++ b/src/devkit/RADevKit.vcxproj.filters
@@ -222,6 +222,9 @@
services
+
+ util
+
@@ -317,5 +320,8 @@
data\models
+
+ util
+
\ No newline at end of file
diff --git a/src/devkit/util/Json.cpp b/src/devkit/util/Json.cpp
new file mode 100644
index 00000000..cab8ac87
--- /dev/null
+++ b/src/devkit/util/Json.cpp
@@ -0,0 +1,489 @@
+#include "Json.hh"
+
+#include "services/impl/FileTextReader.hh"
+#include "services/impl/FileTextWriter.hh"
+#include "services/impl/StringTextReader.hh"
+#include "services/impl/StringTextWriter.hh"
+
+#pragma warning(push, 0) // disable all warnings for these headers
+#pragma warning(disable: ALL_CODE_ANALYSIS_WARNINGS)
+#pragma warning(disable: 6313)
+#pragma warning(disable: 26429)
+#pragma warning(disable: 26432)
+#pragma warning(disable: 26440)
+#pragma warning(disable: 26446)
+#pragma warning(disable: 26447)
+#pragma warning(disable: 26451)
+#pragma warning(disable: 26455)
+#pragma warning(disable: 26457)
+#pragma warning(disable: 26460)
+#pragma warning(disable: 26461)
+#pragma warning(disable: 26462)
+#pragma warning(disable: 26471)
+#pragma warning(disable: 26472)
+#pragma warning(disable: 26473)
+#pragma warning(disable: 26475)
+#pragma warning(disable: 26477)
+#pragma warning(disable: 26490)
+#pragma warning(disable: 26492)
+#pragma warning(disable: 26493)
+#pragma warning(disable: 26494)
+#pragma warning(disable: 26495)
+#pragma warning(disable: 26496)
+#pragma warning(disable: 26497)
+#pragma warning(disable: 26814)
+#pragma warning(disable: 33010)
+
+#define RAPIDJSON_HAS_STDSTRING 1
+#define RAPIDJSON_NOMEMBERITERATORCLASS 1
+#include
+#include
+#include
+#include
+#include // has stringbuffer.h
+
+#pragma warning(pop)
+
+namespace ra {
+namespace util {
+
+class Json::Impl : public rapidjson::Document
+{
+};
+
+Json::Reader::Reader() noexcept
+{
+ // required because of forward declaration of Impl
+}
+
+Json::Reader::~Reader()
+{
+ // required because of forward declaration of Impl
+}
+
+bool Json::Reader::Parse(ra::services::TextReader& pReader)
+{
+ m_pDocument = std::make_unique();
+
+ auto* pFileTextReader = dynamic_cast(&pReader);
+ if (pFileTextReader != nullptr)
+ {
+ auto& iFile = pFileTextReader->GetFStream();
+ if (!iFile.is_open())
+ return false;
+
+ rapidjson::IStreamWrapper iStreamWrapper(iFile);
+ m_pDocument->ParseStream(iStreamWrapper);
+ }
+ else
+ {
+ auto* pStringTextReader = dynamic_cast(&pReader);
+ if (pStringTextReader != nullptr)
+ {
+ m_pDocument->Parse(pStringTextReader->GetString());
+ }
+ else
+ {
+ assert(!"Unsupported TextReader");
+ return false;
+ }
+ }
+
+ return !m_pDocument->HasParseError();
+}
+
+bool Json::Reader::Parse(const std::string& sJson)
+{
+ m_pDocument = std::make_unique();
+ m_pDocument->Parse(sJson);
+
+ return !m_pDocument->HasParseError();
+}
+
+std::string Json::Reader::GetParseError() const
+{
+ return GetParseError_En(m_pDocument->GetParseError());
+}
+
+size_t Json::Reader::GetParseErrorOffset() const
+{
+ return m_pDocument->GetErrorOffset();
+}
+
+std::string Json::Reader::GetString(const std::string& sFieldName, const std::string& sDefaultValue) const
+{
+ std::string sValue;
+ if (!TryGetString(sFieldName, sValue))
+ return sDefaultValue;
+
+ return sValue;
+}
+
+bool Json::Reader::TryGetString(const std::string& sFieldName, std::string& sValue) const
+{
+ const auto* pField = m_pDocument->FindMember(sFieldName);
+ if (pField == m_pDocument->MemberEnd())
+ return false;
+
+ if (!pField->value.IsString())
+ return false;
+
+ sValue.assign(pField->value.GetString(), pField->value.GetStringLength());
+ return true;
+}
+
+int Json::Reader::GetInteger(const std::string& sFieldName, int nDefaultValue) const
+{
+ int nValue;
+ if (!TryGetInteger(sFieldName, nValue))
+ return nDefaultValue;
+
+ return nValue;
+}
+
+bool Json::Reader::TryGetInteger(const std::string& sFieldName, int& nValue) const
+{
+ const auto* pField = m_pDocument->FindMember(sFieldName);
+ if (pField == m_pDocument->MemberEnd())
+ return false;
+
+ if (!pField->value.IsInt())
+ return false;
+
+ nValue = pField->value.GetInt();
+ return true;
+}
+
+bool Json::Reader::GetBoolean(const std::string& sFieldName, bool bDefaultValue) const
+{
+ bool bValue;
+ if (!TryGetBoolean(sFieldName, bValue))
+ return bDefaultValue;
+
+ return bValue;
+}
+
+bool Json::Reader::TryGetBoolean(const std::string& sFieldName, bool& bValue) const
+{
+ const auto* pField = m_pDocument->FindMember(sFieldName);
+ if (pField == m_pDocument->MemberEnd())
+ return false;
+
+ if (!pField->value.IsBool())
+ return false;
+
+ bValue = pField->value.GetBool();
+ return true;
+}
+
+bool Json::Reader::TryGetObject(const std::string& sFieldName, Node& pObject)
+{
+ const auto* pField = m_pDocument->FindMember(sFieldName);
+ if (pField == m_pDocument->MemberEnd())
+ return false;
+
+ if (!pField->value.IsObject())
+ return false;
+
+ pObject.m_pNode = &pField->value;
+ return true;
+}
+
+bool Json::Reader::TryGetObjectArray(const std::string& sFieldName, std::vector& vObjects)
+{
+ const auto* pField = m_pDocument->FindMember(sFieldName);
+ if (pField == m_pDocument->MemberEnd())
+ return false;
+
+ if (!pField->value.IsArray())
+ return false;
+
+ for (const auto& pObject : pField->value.GetArray())
+ {
+ auto& pNode = vObjects.emplace_back();
+ pNode.m_pNode = &pObject;
+ }
+
+ return true;
+}
+
+size_t Json::Reader::GetKeys(std::vector& vKeys) const
+{
+ for (rapidjson::Value::ConstMemberIterator iter = m_pDocument->MemberBegin(); iter != m_pDocument->MemberEnd(); ++iter)
+ vKeys.push_back(iter->name.GetString());
+
+ return vKeys.size();
+}
+
+std::string Json::Reader::Node::GetString(const std::string& sFieldName, const std::string& sDefaultValue) const
+{
+ std::string sValue;
+ if (!TryGetString(sFieldName, sValue))
+ return sDefaultValue;
+
+ return sValue;
+}
+
+bool Json::Reader::Node::TryGetString(const std::string& sFieldName, std::string& sValue) const
+{
+ const auto* pNode = static_cast(m_pNode);
+ Expects(pNode != nullptr);
+
+ const auto* pField = pNode->FindMember(sFieldName);
+ if (pField == pNode->MemberEnd())
+ return false;
+
+ if (!pField->value.IsString())
+ return false;
+
+ sValue.assign(pField->value.GetString(), pField->value.GetStringLength());
+ return true;
+}
+
+int Json::Reader::Node::GetInteger(const std::string& sFieldName, int nDefaultValue) const
+{
+ int nValue;
+ if (!TryGetInteger(sFieldName, nValue))
+ return nDefaultValue;
+
+ return nValue;
+}
+
+bool Json::Reader::Node::TryGetInteger(const std::string& sFieldName, int& nValue) const
+{
+ const auto* pNode = static_cast(m_pNode);
+ Expects(pNode != nullptr);
+
+ const auto* pField = pNode->FindMember(sFieldName);
+ if (pField == pNode->MemberEnd())
+ return false;
+
+ if (!pField->value.IsInt())
+ return false;
+
+ nValue = pField->value.GetInt();
+ return true;
+}
+
+bool Json::Reader::Node::GetBoolean(const std::string& sFieldName, bool bDefaultValue) const
+{
+ bool bValue;
+ if (!TryGetBoolean(sFieldName, bValue))
+ return bDefaultValue;
+
+ return bValue;
+}
+
+bool Json::Reader::Node::TryGetBoolean(const std::string& sFieldName, bool& bValue) const
+{
+ const auto* pNode = static_cast(m_pNode);
+ Expects(pNode != nullptr);
+
+ const auto* pField = pNode->FindMember(sFieldName);
+ if (pField == pNode->MemberEnd())
+ return false;
+
+ if (!pField->value.IsBool())
+ return false;
+
+ bValue = pField->value.GetBool();
+ return true;
+}
+
+bool Json::Reader::Node::TryGetObject(const std::string& sFieldName, Node& pObject)
+{
+ const auto* pNode = static_cast(m_pNode);
+ Expects(pNode != nullptr);
+
+ const auto* pField = pNode->FindMember(sFieldName);
+ if (pField == pNode->MemberEnd())
+ return false;
+
+ if (!pField->value.IsObject())
+ return false;
+
+ pObject.m_pNode = &pField->value;
+ return true;
+}
+
+size_t Json::Reader::Node::GetKeys(std::vector& vKeys) const
+{
+ const auto* pNode = static_cast(m_pNode);
+ Expects(pNode != nullptr);
+
+ for (rapidjson::Value::ConstMemberIterator iter = pNode->MemberBegin(); iter != pNode->MemberEnd(); ++iter)
+ vKeys.push_back(iter->name.GetString());
+
+ return vKeys.size();
+}
+
+GSL_SUPPRESS_F6
+Json::Writer::Writer() noexcept
+{
+ // required because of forward declaration of Impl
+ m_pDocument = std::make_unique();
+ m_pDocument->SetObject();
+}
+
+Json::Writer::~Writer()
+{
+ // required because of forward declaration of Impl
+}
+
+bool Json::Writer::Save(ra::services::TextWriter& pWriter)
+{
+ auto* pFileTextWriter = dynamic_cast(&pWriter);
+ if (pFileTextWriter != nullptr)
+ {
+ auto& oFile = pFileTextWriter->GetFStream();
+ if (!oFile.is_open())
+ return false;
+
+ rapidjson::OStreamWrapper oStreamWrapper(oFile);
+ rapidjson::Writer oStreamWriter(oStreamWrapper);
+ return m_pDocument->Accept(oStreamWriter);
+ }
+
+ auto* pStringTextWriter = dynamic_cast(&pWriter);
+ if (pStringTextWriter != nullptr)
+ {
+ rapidjson::StringBuffer oStringBuffer;
+ rapidjson::Writer oStreamWriter(oStringBuffer);
+ if (!m_pDocument->Accept(oStreamWriter))
+ return false;
+
+ pStringTextWriter->GetString().assign(oStringBuffer.GetString());
+ return true;
+ }
+
+ assert(!"Unsupported TextWriter");
+ return false;
+}
+
+void Json::Writer::SetString(const std::string& sFieldName, const std::string& sValue)
+{
+ auto& a = m_pDocument->GetAllocator();
+ m_pDocument->AddMember(rapidjson::Value(sFieldName, a), rapidjson::Value(sValue, a), a);
+}
+
+void Json::Writer::SetInteger(const std::string& sFieldName, int nValue)
+{
+ auto& a = m_pDocument->GetAllocator();
+ rapidjson::Value pKey(sFieldName, a);
+ m_pDocument->AddMember(pKey, nValue, a);
+}
+
+void Json::Writer::SetBoolean(const std::string& sFieldName, bool bValue)
+{
+ auto& a = m_pDocument->GetAllocator();
+ rapidjson::Value pKey(sFieldName, a);
+ m_pDocument->AddMember(pKey, bValue, a);
+}
+
+Json::Writer::Node Json::Writer::SetObject(const std::string& sFieldName)
+{
+ rapidjson::Value pObject(rapidjson::kObjectType);
+
+ auto& a = m_pDocument->GetAllocator();
+ rapidjson::Value pKey(sFieldName, a);
+ m_pDocument->AddMember(pKey, pObject.Move(), a);
+
+ Node node;
+ node.m_pOwner = this;
+ node.m_pNode = &m_pDocument->FindMember(sFieldName)->value;
+ return node;
+}
+
+Json::Writer::Node Json::Writer::SetObjectArray(const std::string& sFieldName)
+{
+ rapidjson::Value pObject(rapidjson::kArrayType);
+
+ auto& a = m_pDocument->GetAllocator();
+ rapidjson::Value pKey(sFieldName, a);
+ m_pDocument->AddMember(pKey, pObject.Move(), a);
+
+ Node node;
+ node.m_pOwner = this;
+ node.m_pNode = &m_pDocument->FindMember(sFieldName)->value;
+ return node;
+}
+
+void Json::Writer::Node::SetString(const std::string& sFieldName, const std::string& sValue)
+{
+ auto* pNode = static_cast(m_pNode);
+ Expects(pNode != nullptr);
+
+ auto& a = m_pOwner->m_pDocument->GetAllocator();
+ pNode->AddMember(rapidjson::Value(sFieldName, a), rapidjson::Value(sValue, a), a);
+}
+
+void Json::Writer::Node::SetInteger(const std::string& sFieldName, int nValue)
+{
+ auto* pNode = static_cast(m_pNode);
+ Expects(pNode != nullptr);
+
+ auto& a = m_pOwner->m_pDocument->GetAllocator();
+ rapidjson::Value pKey(sFieldName, a);
+ pNode->AddMember(pKey, nValue, a);
+}
+
+void Json::Writer::Node::SetBoolean(const std::string& sFieldName, bool bValue)
+{
+ auto* pNode = static_cast(m_pNode);
+ Expects(pNode != nullptr);
+
+ auto& a = m_pOwner->m_pDocument->GetAllocator();
+ rapidjson::Value pKey(sFieldName, a);
+ pNode->AddMember(pKey, bValue, a);
+}
+
+Json::Writer::Node Json::Writer::Node::SetObject(const std::string& sFieldName)
+{
+ auto* pNode = static_cast(m_pNode);
+ Expects(pNode != nullptr);
+
+ rapidjson::Value pObject(rapidjson::kObjectType);
+
+ auto& a = m_pOwner->m_pDocument->GetAllocator();
+ rapidjson::Value pKey(sFieldName, a);
+ pNode->AddMember(pKey, pObject.Move(), a);
+
+ Node node;
+ node.m_pOwner = m_pOwner;
+ node.m_pNode = &pNode->FindMember(sFieldName)->value;
+ return node;
+}
+
+Json::Writer::Node Json::Writer::Node::SetObjectArray(const std::string& sFieldName)
+{
+ auto* pNode = static_cast(m_pNode);
+ Expects(pNode != nullptr);
+
+ rapidjson::Value pObject(rapidjson::kArrayType);
+
+ auto& a = m_pOwner->m_pDocument->GetAllocator();
+ rapidjson::Value pKey(sFieldName, a);
+ pNode->AddMember(pKey, pObject.Move(), a);
+
+ Node node;
+ node.m_pOwner = m_pOwner;
+ node.m_pNode = &pNode->FindMember(sFieldName)->value;
+ return node;
+}
+
+Json::Writer::Node Json::Writer::Node::AppendObject()
+{
+ auto* pNode = static_cast(m_pNode);
+ Expects(pNode != nullptr);
+
+ rapidjson::Value pObject(rapidjson::kObjectType);
+ pNode->PushBack(pObject, m_pOwner->m_pDocument->GetAllocator());
+
+ Node node;
+ node.m_pOwner = m_pOwner;
+ node.m_pNode = &(*pNode)[pNode->Size() - 1];
+ return node;
+}
+
+} // namespace util
+} // namespace ra
diff --git a/src/devkit/util/Json.hh b/src/devkit/util/Json.hh
new file mode 100644
index 00000000..06af07c5
--- /dev/null
+++ b/src/devkit/util/Json.hh
@@ -0,0 +1,308 @@
+#ifndef RA_UTIL_JSON_HH
+#define RA_UTIL_JSON_HH
+#pragma once
+
+#include
+#include
+#include
+
+#include "services/TextReader.hh"
+#include "services/TextWriter.hh"
+
+#undef GetObject
+
+namespace ra {
+namespace util {
+
+class Json
+{
+private:
+ class Impl;
+
+public:
+ class Reader
+ {
+ public:
+ Reader() noexcept;
+ ~Reader();
+ Reader(const Reader& other) noexcept = delete;
+ Reader& operator=(const Reader&) noexcept = delete;
+ Reader(Reader&& other) noexcept = delete;
+ Reader& operator=(Reader&&) noexcept = delete;
+
+ ///
+ /// Parses the JSON data.
+ ///
+ /// true if parsing was successful, false if not.
+ bool Parse(ra::services::TextReader& pReader);
+
+ ///
+ /// Parses the JSON data.
+ ///
+ /// true if parsing was successful, false if not.
+ bool Parse(const std::string& sJson);
+
+ ///
+ /// Gets a human-readable error message describing why returned false.
+ ///
+ std::string GetParseError() const;
+
+ ///
+ /// Gets the offset within the JSON data where the parse error occured.
+ ///
+ size_t GetParseErrorOffset() const;
+
+ ///
+ /// Gets the string data from the provided field.
+ ///
+ /// Field to query.
+ /// Value to return if the field is not found, or is not a string.
+ std::string GetString(const std::string& sFieldName, const std::string& sDefaultValue = "") const;
+
+ ///
+ /// Tries to gets the string data from the provided field.
+ ///
+ /// Field to query.
+ ///
+ /// String to populate with the data from the specified field.
+ /// It will not be modified if the field is not found or is not a string.
+ ///
+ /// true if the value was populated.
+ bool TryGetString(const std::string& sFieldName, std::string& sValue) const;
+
+ ///
+ /// Gets the integer data from the provided field.
+ ///
+ /// Field to query.
+ /// Value to return if the field is not found, or is not an integer.
+ int GetInteger(const std::string& sFieldName, int nDefaultValue = 0) const;
+
+ ///
+ /// Tries to gets the integer data from the provided field.
+ ///
+ /// Field to query.
+ ///
+ /// Integer to populate with the data from the specified field.
+ /// It will not be modified if the field is not found or is not an integer.
+ ///
+ /// true if the value was populated.
+ bool TryGetInteger(const std::string& sFieldName, int& nValue) const;
+
+ ///
+ /// Gets the integer data from the provided field.
+ ///
+ /// Field to query.
+ /// Value to return if the field is not found, or is not an integer.
+ bool GetBoolean(const std::string& sFieldName, bool bDefaultValue = false) const;
+
+ ///
+ /// Tries to gets the integer data from the provided field.
+ ///
+ /// Field to query.
+ ///
+ /// Boolean to populate with the data from the specified field.
+ /// It will not be modified if the field is not found or is not a boolean.
+ ///
+ /// true if the value was populated.
+ bool TryGetBoolean(const std::string& sFieldName, bool& bValue) const;
+
+ ///
+ /// Gets the available field names.
+ ///
+ /// Number of fields found.
+ size_t GetKeys(std::vector& vKeys) const;
+
+ class Node
+ {
+ public:
+ ///
+ /// Gets the string data from the provided field.
+ ///
+ /// Field to query.
+ /// Value to return if the field is not found, or is not a string.
+ std::string GetString(const std::string& sFieldName, const std::string& sDefaultValue = "") const;
+
+ ///
+ /// Tries to gets the string data from the provided field.
+ ///
+ /// Field to query.
+ ///
+ /// String to populate with the data from the specified field.
+ /// It will not be modified if the field is not found or is not a string.
+ ///
+ /// true if the value was populated.
+ bool TryGetString(const std::string& sFieldName, std::string& sValue) const;
+
+ ///
+ /// Gets the integer data from the provided field.
+ ///
+ /// Field to query.
+ /// Value to return if the field is not found, or is not an integer.
+ int GetInteger(const std::string& sFieldName, int nDefaultValue = 0) const;
+
+ ///
+ /// Tries to gets the integer data from the provided field.
+ ///
+ /// Field to query.
+ ///
+ /// Integer to populate with the data from the specified field.
+ /// It will not be modified if the field is not found or is not an integer.
+ ///
+ /// true if the value was populated.
+ bool TryGetInteger(const std::string& sFieldName, int& nValue) const;
+
+ ///
+ /// Gets the integer data from the provided field.
+ ///
+ /// Field to query.
+ /// Value to return if the field is not found, or is not an integer.
+ bool GetBoolean(const std::string& sFieldName, bool bDefaultValue = false) const;
+
+ ///
+ /// Tries to gets the integer data from the provided field.
+ ///
+ /// Field to query.
+ ///
+ /// Boolean to populate with the data from the specified field.
+ /// It will not be modified if the field is not found or is not a boolean.
+ ///
+ /// true if the value was populated.
+ bool TryGetBoolean(const std::string& sFieldName, bool& bValue) const;
+
+ ///
+ /// Tries to gets the object data from the provided field.
+ ///
+ /// Field to query.
+ ///
+ /// Node to populate with the data from the specified field.
+ /// It will not be modified if the field is not found or is not an object.
+ ///
+ /// true if the value was populated.
+ bool TryGetObject(const std::string& sFieldName, Node& pObject);
+
+ ///
+ /// Gets the available field names.
+ ///
+ /// Number of fields found.
+ size_t GetKeys(std::vector& vKeys) const;
+
+ private:
+ friend class Reader;
+ const void* m_pNode;
+ };
+
+ ///
+ /// Tries to gets the object data from the provided field.
+ ///
+ /// Field to query.
+ ///
+ /// Node to populate with the data from the specified field.
+ /// It will not be modified if the field is not found or is not an object.
+ ///
+ /// true if the value was populated.
+ bool TryGetObject(const std::string& sFieldName, Node& pObject);
+
+ ///
+ /// Tries to gets an array of object data from the provided field.
+ ///
+ /// Field to query.
+ ///
+ /// Array to populate with the data from the specified field.
+ /// It will not be modified if the field is not found or is not an array of objects.
+ ///
+ /// true if the value was populated.
+ bool TryGetObjectArray(const std::string& sFieldName, std::vector& vObjects);
+
+ private:
+ std::unique_ptr m_pDocument;
+ };
+
+ class Writer
+ {
+ public:
+ Writer() noexcept;
+ ~Writer();
+ Writer(const Writer& other) noexcept = delete;
+ Writer& operator=(const Writer&) noexcept = delete;
+ Writer(Writer&& other) noexcept = delete;
+ Writer& operator=(Writer&&) noexcept = delete;
+
+ ///
+ /// Writes the constructed JSON data.
+ ///
+ /// true if writing was successful, false if not.
+ bool Save(ra::services::TextWriter& pWriter);
+
+ ///
+ /// Sets a field to a string value.
+ ///
+ void SetString(const std::string& sFieldName, const std::string& sValue);
+
+ ///
+ /// Sets a field to an integer value.
+ ///
+ void SetInteger(const std::string& sFieldName, int nValue);
+
+ ///
+ /// Sets a field to a boolean value.
+ ///
+ void SetBoolean(const std::string& sFieldName, bool bValue);
+
+ class Node
+ {
+ public:
+ ///
+ /// Sets a field to a string value.
+ ///
+ void SetString(const std::string& sFieldName, const std::string& sValue);
+
+ ///
+ /// Sets a field to an integer value.
+ ///
+ void SetInteger(const std::string& sFieldName, int nValue);
+
+ ///
+ /// Sets a field to a boolean value.
+ ///
+ void SetBoolean(const std::string& sFieldName, bool bValue);
+
+ ///
+ /// Returns an object to set fields in a nested object.
+ ///
+ Node SetObject(const std::string& sFieldName);
+
+ ///
+ /// Returns an object to hold an array of objects.
+ ///
+ Node SetObjectArray(const std::string& sFieldName);
+
+ ///
+ /// Returns an object appended to an array of objects.
+ ///
+ Node AppendObject();
+
+ private:
+ friend class Writer;
+ void* m_pNode;
+ Writer* m_pOwner;
+ };
+
+ ///
+ /// Sets a field to a string value.
+ ///
+ Node SetObject(const std::string& sFieldName);
+
+ ///
+ /// Returns an object to hold an array of objects.
+ ///
+ Node SetObjectArray(const std::string& sFieldName);
+
+ private:
+ std::unique_ptr m_pDocument;
+ };
+};
+
+} // namespace util
+} // namespace ra
+
+#endif // !RA_UTIL_JSON_HH
diff --git a/src/pch.h b/src/pch.h
index 6e310bb3..6ef6c57a 100644
--- a/src/pch.h
+++ b/src/pch.h
@@ -60,15 +60,6 @@
#include "pch_cppcorecheck_suppressions.h"
#include "pch_microsoft_suppressions.h"
-/* RapidJSON Stuff */
-#define RAPIDJSON_HAS_STDSTRING 1
-#define RAPIDJSON_NOMEMBERITERATORCLASS 1
-#include // has reader.h
-#include
-#include
-#include
-#include // has stringbuffer.h
-
/* gsl stuff */
#define GSL_THROW_ON_CONTRACT_VIOLATION
#include
diff --git a/src/services/AchievementRuntime.cpp b/src/services/AchievementRuntime.cpp
index 15f48b51..d4812a38 100644
--- a/src/services/AchievementRuntime.cpp
+++ b/src/services/AchievementRuntime.cpp
@@ -3,9 +3,9 @@
#include "AchievementRuntimeExports.hh"
#include "Exports.hh"
#include "RA_Defs.h"
-#include "RA_Json.h"
#include "RA_Resource.h"
#include "util\EnumOps.hh"
+#include "util\Json.hh"
#include "util\Log.hh"
#include "util\Strings.hh"
@@ -502,14 +502,17 @@ static void ProcessPatchData(const rc_api_server_response_t* server_response,
auto pOldData = pLocalStorage.ReadText(StorageItemType::GameData, std::to_wstring(pGameContext.ActiveGameId()));
if (pOldData != nullptr)
{
- rapidjson::Document pDocument;
- if (LoadDocument(pDocument, *pOldData) && pDocument.HasMember("RichPresencePatch") &&
- pDocument["RichPresencePatch"].IsString())
+ ra::util::Json::Reader pJson;
+ if (pJson.Parse(*pOldData))
{
- auto* pRichPresence = pGameContext.Assets().FindRichPresence();
- Expects(pRichPresence != nullptr);
- pRichPresence->SetScript(pDocument["RichPresencePatch"].GetString());
- pRichPresence->UpdateLocalCheckpoint();
+ std::string sRichPresencePatch;
+ if (pJson.TryGetString("RichPresencePatch", sRichPresencePatch))
+ {
+ auto* pRichPresence = pGameContext.Assets().FindRichPresence();
+ Expects(pRichPresence != nullptr);
+ pRichPresence->SetScript(sRichPresencePatch);
+ pRichPresence->UpdateLocalCheckpoint();
+ }
}
}
diff --git a/src/services/impl/FileLocalStorage.cpp b/src/services/impl/FileLocalStorage.cpp
index d47b65ee..e7326e29 100644
--- a/src/services/impl/FileLocalStorage.cpp
+++ b/src/services/impl/FileLocalStorage.cpp
@@ -1,6 +1,5 @@
#include "FileLocalStorage.hh"
-#include "RA_Json.h"
#include "util\Log.hh"
#include "util\Strings.hh"
diff --git a/src/services/impl/JsonFileConfiguration.cpp b/src/services/impl/JsonFileConfiguration.cpp
index dab8b33e..07eff919 100644
--- a/src/services/impl/JsonFileConfiguration.cpp
+++ b/src/services/impl/JsonFileConfiguration.cpp
@@ -1,6 +1,6 @@
#include "JsonFileConfiguration.hh"
-#include "RA_Json.h"
+#include "util\Json.hh"
#include "util\Log.hh"
#include "util\Strings.hh"
@@ -20,45 +20,41 @@ namespace services {
namespace impl {
static void ReadPopupLocation(JsonFileConfiguration& pConfiguration, ra::ui::viewmodels::Popup nPopup,
- const rapidjson::Document& doc, const char* sFieldName, ra::ui::viewmodels::PopupLocation nDefaultLocation, bool bAllowCenter)
+ const ra::util::Json::Reader& doc, const char* sFieldName, ra::ui::viewmodels::PopupLocation nDefaultLocation, bool bAllowCenter)
{
ra::ui::viewmodels::PopupLocation nLocation = nDefaultLocation;
- if (doc.HasMember(sFieldName))
+ std::string sValue;
+ bool bValue;
+
+ if (doc.TryGetString(sFieldName, sValue) && !sValue.empty())
{
- const auto& pField = doc[sFieldName];
- if (pField.IsBool())
+ if (sValue.at(0) == 'T')
{
- if (!pField.GetBool())
- nLocation = ra::ui::viewmodels::PopupLocation::None;
+ if (sValue == "TopLeft")
+ nLocation = ra::ui::viewmodels::PopupLocation::TopLeft;
+ else if (sValue == "TopRight")
+ nLocation = ra::ui::viewmodels::PopupLocation::TopRight;
+ else if (bAllowCenter && sValue == "TopMiddle")
+ nLocation = ra::ui::viewmodels::PopupLocation::TopMiddle;
}
- else if (pField.IsString())
+ else if (sValue.at(0) == 'B')
{
- const char* pValue = pField.GetString();
- Expects(pValue != nullptr);
- if (*pValue == 'T')
- {
- if (strcmp(pValue, "TopLeft") == 0)
- nLocation = ra::ui::viewmodels::PopupLocation::TopLeft;
- else if (strcmp(pValue, "TopRight") == 0)
- nLocation = ra::ui::viewmodels::PopupLocation::TopRight;
- else if (bAllowCenter && strcmp(pValue, "TopMiddle") == 0)
- nLocation = ra::ui::viewmodels::PopupLocation::TopMiddle;
- }
- else if (*pValue == 'B')
- {
- if (strcmp(pValue, "BottomLeft") == 0)
- nLocation = ra::ui::viewmodels::PopupLocation::BottomLeft;
- else if (strcmp(pValue, "BottomRight") == 0)
- nLocation = ra::ui::viewmodels::PopupLocation::BottomRight;
- else if (bAllowCenter && strcmp(pValue, "BottomMiddle") == 0)
- nLocation = ra::ui::viewmodels::PopupLocation::BottomMiddle;
- }
- else if (strcmp(pValue, "None") == 0)
- {
- nLocation = ra::ui::viewmodels::PopupLocation::None;
- }
+ if (sValue == "BottomLeft")
+ nLocation = ra::ui::viewmodels::PopupLocation::BottomLeft;
+ else if (sValue == "BottomRight")
+ nLocation = ra::ui::viewmodels::PopupLocation::BottomRight;
+ else if (bAllowCenter && sValue == "BottomMiddle")
+ nLocation = ra::ui::viewmodels::PopupLocation::BottomMiddle;
}
+ else if (sValue == "None")
+ {
+ nLocation = ra::ui::viewmodels::PopupLocation::None;
+ }
+ }
+ else if (doc.TryGetBoolean(sFieldName, bValue) && !bValue)
+ {
+ nLocation = ra::ui::viewmodels::PopupLocation::None;
}
pConfiguration.SetPopupLocation(nPopup, nLocation);
@@ -93,66 +89,55 @@ bool JsonFileConfiguration::Load(const std::wstring& sFilename)
if (pReader == nullptr)
return false;
- rapidjson::Document doc;
- if (!LoadDocument(doc, *pReader))
+ ra::util::Json::Reader pJson;
+ if (!pJson.Parse(*pReader))
return false;
- if (doc.HasMember("Username"))
- m_sUsername = doc["Username"].GetString();
- if (doc.HasMember("Token"))
- m_sApiToken = doc["Token"].GetString();
- if (doc.HasMember("Hardcore Active"))
- SetFeatureEnabled(Feature::Hardcore, doc["Hardcore Active"].GetBool());
- if (doc.HasMember("Non Hardcore Warning"))
- SetFeatureEnabled(Feature::NonHardcoreWarning, doc["Non Hardcore Warning"].GetBool());
- if (doc.HasMember("Only Hardcore Unlocks"))
- SetFeatureEnabled(Feature::OnlyHardcoreUnlocks, doc["Only Hardcore Unlocks"].GetBool());
-
- ReadPopupLocation(*this, ra::ui::viewmodels::Popup::AchievementTriggered, doc, "Achievement Triggered Notification Display", ra::ui::viewmodels::PopupLocation::BottomLeft, true);
- if (doc.HasMember("Achievement Triggered Screenshot"))
- SetFeatureEnabled(Feature::AchievementTriggeredScreenshot, doc["Achievement Triggered Screenshot"].GetBool());
- ReadPopupLocation(*this, ra::ui::viewmodels::Popup::Mastery, doc, "Mastery Notification Display", ra::ui::viewmodels::PopupLocation::TopMiddle, true);
- if (doc.HasMember("Mastery Screenshot"))
- SetFeatureEnabled(Feature::MasteryNotificationScreenshot, doc["Mastery Screenshot"].GetBool());
- if (doc.HasMember("Screenshot Directory"))
- SetScreenshotDirectory(ra::util::String::Widen(doc["Screenshot Directory"].GetString()));
-
- if (doc.HasMember("Leaderboards Active"))
- SetFeatureEnabled(Feature::Leaderboards, doc["Leaderboards Active"].GetBool());
- ReadPopupLocation(*this, ra::ui::viewmodels::Popup::LeaderboardStarted, doc, "Leaderboard Notification Display", ra::ui::viewmodels::PopupLocation::BottomLeft, true);
- ReadPopupLocation(*this, ra::ui::viewmodels::Popup::LeaderboardCanceled, doc, "Leaderboard Cancel Display", ra::ui::viewmodels::PopupLocation::BottomLeft, true);
- ReadPopupLocation(*this, ra::ui::viewmodels::Popup::LeaderboardTracker, doc, "Leaderboard Counter Display", ra::ui::viewmodels::PopupLocation::BottomRight, true);
- ReadPopupLocation(*this, ra::ui::viewmodels::Popup::LeaderboardScoreboard, doc, "Leaderboard Scoreboard Display", ra::ui::viewmodels::PopupLocation::BottomRight, false);
-
- ReadPopupLocation(*this, ra::ui::viewmodels::Popup::Challenge, doc, "Challenge Notification Display", ra::ui::viewmodels::PopupLocation::BottomRight, false);
- ReadPopupLocation(*this, ra::ui::viewmodels::Popup::Progress, doc, "Progress Display", ra::ui::viewmodels::PopupLocation::BottomRight, false);
-
- ReadPopupLocation(*this, ra::ui::viewmodels::Popup::Message, doc, "Informational Notification Display", ra::ui::viewmodels::PopupLocation::BottomLeft, true);
-
- if (doc.HasMember("Prefer Decimal"))
- SetFeatureEnabled(Feature::PreferDecimal, doc["Prefer Decimal"].GetBool());
-
- if (doc.HasMember("Num Background Threads"))
- m_nBackgroundThreads = doc["Num Background Threads"].GetUint();
-
- if (doc.HasMember("Window Positions"))
+ m_sUsername = pJson.GetString("Username");
+ m_sApiToken = pJson.GetString("Token");
+ SetFeatureEnabled(Feature::Hardcore, pJson.GetBoolean("Hardcore Active", true));
+ SetFeatureEnabled(Feature::NonHardcoreWarning, pJson.GetBoolean("Non Hardcore Warning", false));
+ SetFeatureEnabled(Feature::OnlyHardcoreUnlocks, pJson.GetBoolean("Only Hardcore Unlocks", false));
+
+ ReadPopupLocation(*this, ra::ui::viewmodels::Popup::AchievementTriggered, pJson, "Achievement Triggered Notification Display", ra::ui::viewmodels::PopupLocation::BottomLeft, true);
+ SetFeatureEnabled(Feature::AchievementTriggeredScreenshot, pJson.GetBoolean("Achievement Triggered Screenshot", false));
+ ReadPopupLocation(*this, ra::ui::viewmodels::Popup::Mastery, pJson, "Mastery Notification Display", ra::ui::viewmodels::PopupLocation::TopMiddle, true);
+ SetFeatureEnabled(Feature::MasteryNotificationScreenshot, pJson.GetBoolean("Mastery Screenshot", false));
+
+ std::string sScreenShotDirectory;
+ if (pJson.TryGetString("Screenshot Directory", sScreenShotDirectory))
+ SetScreenshotDirectory(ra::util::String::Widen(sScreenShotDirectory));
+
+ SetFeatureEnabled(Feature::Leaderboards, pJson.GetBoolean("Leaderboards Active", true));
+ ReadPopupLocation(*this, ra::ui::viewmodels::Popup::LeaderboardStarted, pJson, "Leaderboard Notification Display", ra::ui::viewmodels::PopupLocation::BottomLeft, true);
+ ReadPopupLocation(*this, ra::ui::viewmodels::Popup::LeaderboardCanceled, pJson, "Leaderboard Cancel Display", ra::ui::viewmodels::PopupLocation::BottomLeft, true);
+ ReadPopupLocation(*this, ra::ui::viewmodels::Popup::LeaderboardTracker, pJson, "Leaderboard Counter Display", ra::ui::viewmodels::PopupLocation::BottomRight, true);
+ ReadPopupLocation(*this, ra::ui::viewmodels::Popup::LeaderboardScoreboard, pJson, "Leaderboard Scoreboard Display", ra::ui::viewmodels::PopupLocation::BottomRight, false);
+
+ ReadPopupLocation(*this, ra::ui::viewmodels::Popup::Challenge, pJson, "Challenge Notification Display", ra::ui::viewmodels::PopupLocation::BottomRight, false);
+ ReadPopupLocation(*this, ra::ui::viewmodels::Popup::Progress, pJson, "Progress Display", ra::ui::viewmodels::PopupLocation::BottomRight, false);
+
+ ReadPopupLocation(*this, ra::ui::viewmodels::Popup::Message, pJson, "Informational Notification Display", ra::ui::viewmodels::PopupLocation::BottomLeft, true);
+
+ SetFeatureEnabled(Feature::PreferDecimal, pJson.GetBoolean("Prefer Decimal", false));
+
+ m_nBackgroundThreads = pJson.GetInteger("Num Background Threads", 4);
+
+ ra::util::Json::Reader::Node pWindowPositions;
+ if (pJson.TryGetObject("Window Positions", pWindowPositions))
{
- const rapidjson::Value& positions = doc["Window Positions"];
- if (positions.IsObject())
+ std::vector vKeys;
+ pWindowPositions.GetKeys(vKeys);
+ for (const auto& sKey : vKeys)
{
- for (rapidjson::Value::ConstMemberIterator iter = positions.MemberBegin(); iter != positions.MemberEnd(); ++iter)
+ ra::util::Json::Reader::Node pWindowPosition;
+ if (pWindowPositions.TryGetObject(sKey, pWindowPosition))
{
- WindowPosition& pos = m_mWindowPositions[iter->name.GetString()];
- pos.oPosition.X = pos.oPosition.Y = pos.oSize.Width = pos.oSize.Height = INT32_MIN;
-
- if (iter->value.HasMember("X"))
- pos.oPosition.X = iter->value["X"].GetInt();
- if (iter->value.HasMember("Y"))
- pos.oPosition.Y = iter->value["Y"].GetInt();
- if (iter->value.HasMember("Width"))
- pos.oSize.Width = iter->value["Width"].GetInt();
- if (iter->value.HasMember("Height"))
- pos.oSize.Height = iter->value["Height"].GetInt();
+ WindowPosition& pos = m_mWindowPositions[sKey];
+ pos.oPosition.X = pWindowPosition.GetInteger("X", INT32_MIN);
+ pos.oPosition.Y = pWindowPosition.GetInteger("Y", INT32_MIN);
+ pos.oSize.Width = pWindowPosition.GetInteger("Width", INT32_MIN);
+ pos.oSize.Height = pWindowPosition.GetInteger("Height", INT32_MIN);
}
}
}
@@ -160,31 +145,31 @@ bool JsonFileConfiguration::Load(const std::wstring& sFilename)
return true;
}
-static void WritePopupLocation(rapidjson::Document& doc, rapidjson::Document::AllocatorType& a,
- const char* sFieldName, ra::ui::viewmodels::PopupLocation nPopupLocation)
+static void WritePopupLocation(ra::util::Json::Writer& pWriter,
+ const std::string& sFieldName, ra::ui::viewmodels::PopupLocation nPopupLocation)
{
switch (nPopupLocation)
{
case ra::ui::viewmodels::PopupLocation::None:
- doc.AddMember(rapidjson::StringRef(sFieldName), rapidjson::StringRef("None"), a);
+ pWriter.SetString(sFieldName, "None");
break;
case ra::ui::viewmodels::PopupLocation::TopLeft:
- doc.AddMember(rapidjson::StringRef(sFieldName), rapidjson::StringRef("TopLeft"), a);
+ pWriter.SetString(sFieldName, "TopLeft");
break;
case ra::ui::viewmodels::PopupLocation::TopMiddle:
- doc.AddMember(rapidjson::StringRef(sFieldName), rapidjson::StringRef("TopMiddle"), a);
+ pWriter.SetString(sFieldName, "TopMiddle");
break;
case ra::ui::viewmodels::PopupLocation::TopRight:
- doc.AddMember(rapidjson::StringRef(sFieldName), rapidjson::StringRef("TopRight"), a);
+ pWriter.SetString(sFieldName, "TopRight");
break;
case ra::ui::viewmodels::PopupLocation::BottomLeft:
- doc.AddMember(rapidjson::StringRef(sFieldName), rapidjson::StringRef("BottomLeft"), a);
+ pWriter.SetString(sFieldName, "BottomLeft");
break;
case ra::ui::viewmodels::PopupLocation::BottomMiddle:
- doc.AddMember(rapidjson::StringRef(sFieldName), rapidjson::StringRef("BottomMiddle"), a);
+ pWriter.SetString(sFieldName, "BottomMiddle");
break;
case ra::ui::viewmodels::PopupLocation::BottomRight:
- doc.AddMember(rapidjson::StringRef(sFieldName), rapidjson::StringRef("BottomRight"), a);
+ pWriter.SetString(sFieldName, "BottomRight");
break;
}
}
@@ -199,56 +184,51 @@ void JsonFileConfiguration::Save() const
return;
}
- rapidjson::Document doc;
- doc.SetObject();
-
- rapidjson::Document::AllocatorType& a = doc.GetAllocator();
- doc.AddMember("Username", rapidjson::StringRef(m_sUsername), a);
- doc.AddMember("Token", rapidjson::StringRef(m_sApiToken), a);
- doc.AddMember("Hardcore Active", IsFeatureEnabled(Feature::Hardcore), a);
- doc.AddMember("Non Hardcore Warning", IsFeatureEnabled(Feature::NonHardcoreWarning), a);
- doc.AddMember("Only Hardcore Unlocks", IsFeatureEnabled(Feature::OnlyHardcoreUnlocks), a);
- WritePopupLocation(doc, a, "Achievement Triggered Notification Display", GetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered));
- doc.AddMember("Achievement Triggered Screenshot", IsFeatureEnabled(Feature::AchievementTriggeredScreenshot), a);
- WritePopupLocation(doc, a, "Mastery Notification Display", GetPopupLocation(ra::ui::viewmodels::Popup::Mastery));
- doc.AddMember("Mastery Screenshot", IsFeatureEnabled(Feature::MasteryNotificationScreenshot), a);
- doc.AddMember("Leaderboards Active", IsFeatureEnabled(Feature::Leaderboards), a);
- WritePopupLocation(doc, a, "Leaderboard Notification Display", GetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardStarted));
- WritePopupLocation(doc, a, "Leaderboard Cancel Display", GetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardCanceled));
- WritePopupLocation(doc, a, "Leaderboard Counter Display", GetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardTracker));
- WritePopupLocation(doc, a, "Leaderboard Scoreboard Display", GetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardScoreboard));
- WritePopupLocation(doc, a, "Challenge Notification Display", GetPopupLocation(ra::ui::viewmodels::Popup::Challenge));
- WritePopupLocation(doc, a, "Informational Notification Display", GetPopupLocation(ra::ui::viewmodels::Popup::Message));
- doc.AddMember("Prefer Decimal", IsFeatureEnabled(Feature::PreferDecimal), a);
- doc.AddMember("Num Background Threads", m_nBackgroundThreads, a);
+ ra::util::Json::Writer pWriter;
+ pWriter.SetString("Username", m_sUsername);
+ pWriter.SetString("Token", m_sApiToken);
+
+ pWriter.SetBoolean("Hardcore Active", IsFeatureEnabled(Feature::Hardcore));
+ pWriter.SetBoolean("Non Hardcore Warning", IsFeatureEnabled(Feature::NonHardcoreWarning));
+ pWriter.SetBoolean("Only Hardcore Unlocks", IsFeatureEnabled(Feature::OnlyHardcoreUnlocks));
+
+ WritePopupLocation(pWriter, "Achievement Triggered Notification Display", GetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered));
+ pWriter.SetBoolean("Achievement Triggered Screenshot", IsFeatureEnabled(Feature::AchievementTriggeredScreenshot));
+ WritePopupLocation(pWriter, "Mastery Notification Display", GetPopupLocation(ra::ui::viewmodels::Popup::Mastery));
+ pWriter.SetBoolean("Mastery Screenshot", IsFeatureEnabled(Feature::MasteryNotificationScreenshot));
+
+ pWriter.SetBoolean("Leaderboards Active", IsFeatureEnabled(Feature::Leaderboards));
+ WritePopupLocation(pWriter, "Leaderboard Notification Display", GetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardStarted));
+ WritePopupLocation(pWriter, "Leaderboard Cancel Display", GetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardCanceled));
+ WritePopupLocation(pWriter, "Leaderboard Counter Display", GetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardTracker));
+ WritePopupLocation(pWriter, "Leaderboard Scoreboard Display", GetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardScoreboard));
+ WritePopupLocation(pWriter, "Challenge Notification Display", GetPopupLocation(ra::ui::viewmodels::Popup::Challenge));
+ WritePopupLocation(pWriter, "Informational Notification Display", GetPopupLocation(ra::ui::viewmodels::Popup::Message));
+
+ pWriter.SetBoolean("Prefer Decimal", IsFeatureEnabled(Feature::PreferDecimal));
+ pWriter.SetInteger("Num Background Threads", m_nBackgroundThreads);
if (!m_sScreenshotDirectory.empty())
- doc.AddMember("Screenshot Directory", ra::util::String::Narrow(m_sScreenshotDirectory), a);
+ pWriter.SetString("Screenshot Directory", ra::util::String::Narrow(m_sScreenshotDirectory));
- rapidjson::Value positions(rapidjson::kObjectType);
+ ra::util::Json::Writer::Node pWindowPositions = pWriter.SetObject("Window Positions");
for (WindowPositionMap::const_iterator iter = m_mWindowPositions.begin(); iter != m_mWindowPositions.end(); ++iter)
{
- rapidjson::Value rect(rapidjson::kObjectType);
+ ra::util::Json::Writer::Node pWindowPosition = pWindowPositions.SetObject(iter->first);
if (iter->second.oPosition.X != INT32_MIN)
- rect.AddMember("X", iter->second.oPosition.X, a);
+ pWindowPosition.SetInteger("X", iter->second.oPosition.X);
if (iter->second.oPosition.Y != INT32_MIN)
- rect.AddMember("Y", iter->second.oPosition.Y, a);
+ pWindowPosition.SetInteger("Y", iter->second.oPosition.Y);
if (iter->second.oSize.Width != INT32_MIN)
- rect.AddMember("Width", iter->second.oSize.Width, a);
+ pWindowPosition.SetInteger("Width", iter->second.oSize.Width);
if (iter->second.oSize.Height != INT32_MIN)
- rect.AddMember("Height", iter->second.oSize.Height, a);
-
- if (rect.MemberCount() > 0)
- positions.AddMember(rapidjson::StringRef(iter->first), rect, a);
+ pWindowPosition.SetInteger("Height", iter->second.oSize.Height);
}
- if (positions.MemberCount() > 0)
- doc.AddMember("Window Positions", positions.Move(), a);
-
auto& pFileSystem = ra::services::ServiceLocator::Get();
- auto pWriter = pFileSystem.CreateTextFile(m_sFilename);
- if (pWriter != nullptr)
- SaveDocument(doc, *pWriter);
+ auto pFile = pFileSystem.CreateTextFile(m_sFilename);
+ if (pFile != nullptr)
+ pWriter.Save(*pFile);
}
bool JsonFileConfiguration::IsFeatureEnabled(Feature nFeature) const noexcept
diff --git a/src/ui/Theme.cpp b/src/ui/Theme.cpp
index 72ad458d..c210d729 100644
--- a/src/ui/Theme.cpp
+++ b/src/ui/Theme.cpp
@@ -1,7 +1,7 @@
#include "EditorTheme.hh"
#include "OverlayTheme.hh"
-#include "RA_Json.h"
+#include "util\Json.hh"
#include "util\Log.hh"
#include "services\IFileSystem.hh"
@@ -10,44 +10,20 @@
namespace ra {
namespace ui {
-static void ReadSize(int& nSize, const rapidjson::Value& pSizes, const char* pJsonField)
+static void ReadColor(Color& nColor, const ra::util::Json::Reader::Node& pColors, const std::string& sFieldName)
{
- if (pSizes.HasMember(pJsonField))
+ std::string sValue;
+ if (pColors.TryGetString(sFieldName, sValue))
{
- const auto& pField = pSizes[pJsonField];
- if (pField.IsInt())
- nSize = pField.GetInt();
- }
-}
-
-static void ReadBool(bool& bValue, const rapidjson::Value& pContainer, const char* pJsonField)
-{
- if (pContainer.HasMember(pJsonField))
- {
- const auto& pField = pContainer[pJsonField];
- if (pField.IsBool())
- bValue = pField.GetBool();
- }
-}
+ if (sValue.length() == 7 && sValue.at(0) == '#')
+ sValue.erase(sValue.begin());
-static void ReadColor(Color& nColor, const rapidjson::Value& pColors, const char* pJsonField)
-{
- if (pColors.HasMember(pJsonField))
- {
- const auto& pField = pColors[pJsonField];
- if (pField.IsString())
+ if (sValue.length() == 6)
{
- std::string sValue = pField.GetString();
- if (sValue.length() == 7 && sValue.at(0) == '#')
- sValue.erase(sValue.begin());
-
- if (sValue.length() == 6)
- {
- char* pEnd;
- const auto nValue = strtoul(sValue.c_str(), &pEnd, 16);
- if (pEnd && *pEnd == '\0')
- nColor = Color(nValue | 0xFF000000);
- }
+ char* pEnd;
+ const auto nValue = strtoul(sValue.c_str(), &pEnd, 16);
+ if (pEnd && *pEnd == '\0')
+ nColor = Color(nValue | 0xFF000000);
}
}
}
@@ -63,86 +39,77 @@ void OverlayTheme::LoadFromFile()
if (!pFile)
return;
- rapidjson::Document document;
- if (!LoadDocument(document, *pFile))
+ ra::util::Json::Reader pJson;
+ if (!pJson.Parse(*pFile))
{
- RA_LOG_ERR("Unable to read Overlay\\theme.json: %s (%zu)",
- GetParseError_En(document.GetParseError()), document.GetErrorOffset());
+ RA_LOG_ERR("Unable to read %s: %s (%zu)", L"Overlay\\theme.json", pJson.GetParseError(), pJson.GetParseErrorOffset());
return;
}
- if (document.HasMember("Popup"))
+ ra::util::Json::Reader::Node pPopup;
+ if (pJson.TryGetObject("Popup", pPopup))
{
- const rapidjson::Value& popup = document["Popup"];
-
- if (popup.HasMember("Font"))
- m_sFontPopup = popup["Font"].GetString();
+ pPopup.TryGetString("Font", m_sFontPopup);
- if (popup.HasMember("FontSizes"))
+ ra::util::Json::Reader::Node pFontSizes;
+ if (pPopup.TryGetObject("FontSizes", pFontSizes))
{
- const rapidjson::Value& sizes = popup["FontSizes"];
-
- ReadSize(m_nFontSizePopupTitle, sizes, "Title");
- ReadSize(m_nFontSizePopupSubtitle, sizes, "Subtitle");
- ReadSize(m_nFontSizePopupDetail, sizes, "Detail");
- ReadSize(m_nFontSizePopupLeaderboardTitle, sizes, "LeaderboardTitle");
- ReadSize(m_nFontSizePopupLeaderboardEntry, sizes, "LeaderboardEntry");
- ReadSize(m_nFontSizePopupLeaderboardTracker, sizes, "LeaderboardTracker");
+ pFontSizes.TryGetInteger("Title", m_nFontSizePopupTitle);
+ pFontSizes.TryGetInteger("Subtitle", m_nFontSizePopupSubtitle);
+ pFontSizes.TryGetInteger("Detail", m_nFontSizePopupDetail);
+ pFontSizes.TryGetInteger("LeaderboardTitle", m_nFontSizePopupLeaderboardTitle);
+ pFontSizes.TryGetInteger("LeaderboardEntry", m_nFontSizePopupLeaderboardEntry);
+ pFontSizes.TryGetInteger("LeaderboardTracker", m_nFontSizePopupLeaderboardTracker);
}
- if (popup.HasMember("Colors"))
+ ra::util::Json::Reader::Node pColors;
+ if (pPopup.TryGetObject("Colors", pColors))
{
- const rapidjson::Value& colors = popup["Colors"];
-
- ReadColor(m_colorBackground, colors, "Background");
- ReadColor(m_colorMasteryBackground, colors, "MasteryBackground");
- ReadColor(m_colorNonHardcoreBackground, colors, "NonHardcoreBackground");
- ReadColor(m_colorBorder, colors, "Border");
- ReadColor(m_colorTextShadow, colors, "TextShadow");
- ReadColor(m_colorTitle, colors, "Title");
- ReadColor(m_colorDescription, colors, "Description");
- ReadColor(m_colorDetail, colors, "Detail");
- ReadColor(m_colorError, colors, "Error");
- ReadColor(m_colorLeaderboardEntry, colors, "LeaderboardEntry");
- ReadColor(m_colorLeaderboardPlayer, colors, "LeaderboardPlayer");
+ ReadColor(m_colorBackground, pColors, "Background");
+ ReadColor(m_colorMasteryBackground, pColors, "MasteryBackground");
+ ReadColor(m_colorNonHardcoreBackground, pColors, "NonHardcoreBackground");
+ ReadColor(m_colorBorder, pColors, "Border");
+ ReadColor(m_colorTextShadow, pColors, "TextShadow");
+ ReadColor(m_colorTitle, pColors, "Title");
+ ReadColor(m_colorDescription, pColors, "Description");
+ ReadColor(m_colorDetail, pColors, "Detail");
+ ReadColor(m_colorError, pColors, "Error");
+ ReadColor(m_colorLeaderboardEntry, pColors, "LeaderboardEntry");
+ ReadColor(m_colorLeaderboardPlayer, pColors, "LeaderboardPlayer");
}
}
- if (document.HasMember("Overlay"))
+ ra::util::Json::Reader::Node pOverlay;
+ if (pJson.TryGetObject("Overlay", pOverlay))
{
- const rapidjson::Value& overlay = document["Overlay"];
+ pOverlay.TryGetString("Font", m_sFontOverlay);
- if (overlay.HasMember("Font"))
- m_sFontOverlay = overlay["Font"].GetString();
-
- if (overlay.HasMember("FontSizes"))
+ ra::util::Json::Reader::Node pFontSizes;
+ if (pOverlay.TryGetObject("FontSizes", pFontSizes))
{
- const rapidjson::Value& sizes = overlay["FontSizes"];
-
- ReadSize(m_nFontSizeOverlayTitle, sizes, "Title");
- ReadSize(m_nFontSizeOverlayHeader, sizes, "Header");
- ReadSize(m_nFontSizeOverlaySummary, sizes, "Summary");
- ReadSize(m_nFontSizeOverlayDetail, sizes, "Detail");
+ pFontSizes.TryGetInteger("Title", m_nFontSizeOverlayTitle);
+ pFontSizes.TryGetInteger("Header", m_nFontSizeOverlayHeader);
+ pFontSizes.TryGetInteger("Summary", m_nFontSizeOverlaySummary);
+ pFontSizes.TryGetInteger("Detail", m_nFontSizeOverlayDetail);
}
- if (overlay.HasMember("Colors"))
+ ra::util::Json::Reader::Node pColors;
+ if (pOverlay.TryGetObject("Colors", pColors))
{
- const rapidjson::Value& colors = overlay["Colors"];
-
- ReadColor(m_colorOverlayPanel, colors, "Panel");
- ReadColor(m_colorOverlayText, colors, "Text");
- ReadColor(m_colorOverlayDisabledText, colors, "DisabledText");
- ReadColor(m_colorOverlaySubText, colors, "SubText");
- ReadColor(m_colorOverlayDisabledSubText, colors, "DisabledSubText");
- ReadColor(m_colorOverlaySelectionBackground, colors, "SelectionBackground");
- ReadColor(m_colorOverlaySelectionText, colors, "SelectionText");
- ReadColor(m_colorOverlaySelectionDisabledText, colors, "SelectionDisabledText");
- ReadColor(m_colorOverlayScrollBar, colors, "ScrollBar");
- ReadColor(m_colorOverlayScrollBarGripper, colors, "ScrollBarGripper");
+ ReadColor(m_colorOverlayPanel, pColors, "Panel");
+ ReadColor(m_colorOverlayText, pColors, "Text");
+ ReadColor(m_colorOverlayDisabledText, pColors, "DisabledText");
+ ReadColor(m_colorOverlaySubText, pColors, "SubText");
+ ReadColor(m_colorOverlayDisabledSubText, pColors, "DisabledSubText");
+ ReadColor(m_colorOverlaySelectionBackground, pColors, "SelectionBackground");
+ ReadColor(m_colorOverlaySelectionText, pColors, "SelectionText");
+ ReadColor(m_colorOverlaySelectionDisabledText, pColors, "SelectionDisabledText");
+ ReadColor(m_colorOverlayScrollBar, pColors, "ScrollBar");
+ ReadColor(m_colorOverlayScrollBarGripper, pColors, "ScrollBarGripper");
}
}
- ReadBool(m_bTransparent, document, "Transparent");
+ pJson.TryGetBoolean("Transparent", m_bTransparent);
}
void EditorTheme::LoadFromFile()
@@ -156,63 +123,55 @@ void EditorTheme::LoadFromFile()
if (!pFile)
return;
- rapidjson::Document document;
- if (!LoadDocument(document, *pFile))
+ ra::util::Json::Reader pJson;
+ if (!pJson.Parse(*pFile))
{
- RA_LOG_ERR("Unable to read Overlay\\editor_theme.json: %s (%zu)",
- GetParseError_En(document.GetParseError()), document.GetErrorOffset());
+ RA_LOG_ERR("Unable to read %s: %s (%zu)", L"Overlay\\editor_theme.json", pJson.GetParseError(), pJson.GetParseErrorOffset());
return;
}
- if (document.HasMember("MemoryViewer"))
+ ra::util::Json::Reader::Node pMemoryViewer;
+ if (pJson.TryGetObject("MemoryViewer", pMemoryViewer))
{
- const rapidjson::Value& memoryViewer = document["MemoryViewer"];
+ pMemoryViewer.TryGetString("Font", m_sFontMemoryViewer);
+ pMemoryViewer.TryGetInteger("FontSize", m_nFontSizeMemoryViewer);
- if (memoryViewer.HasMember("Font"))
- m_sFontMemoryViewer = memoryViewer["Font"].GetString();
-
- if (memoryViewer.HasMember("FontSize"))
- ReadSize(m_nFontSizeMemoryViewer, memoryViewer, "FontSize");
-
- if (memoryViewer.HasMember("Colors"))
+ ra::util::Json::Reader::Node pColors;
+ if (pMemoryViewer.TryGetObject("Colors", pColors))
{
- const rapidjson::Value& colors = memoryViewer["Colors"];
-
- ReadColor(m_colorBackground, colors, "Background");
- ReadColor(m_colorSeparator, colors, "Separator");
- ReadColor(m_colorCursor, colors, "Cursor");
- ReadColor(m_colorNormal, colors, "Normal");
- ReadColor(m_colorSelected, colors, "Selected");
- ReadColor(m_colorHasNote, colors, "HasNote");
- ReadColor(m_colorHasSurrogateNote, colors, "HasSurrogateNote");
- ReadColor(m_colorHasBookmark, colors, "HasBookmark");
- ReadColor(m_colorFrozen, colors, "Frozen");
- ReadColor(m_colorHeader, colors, "Header");
- ReadColor(m_colorHeaderSelected, colors, "HeaderSelected");
+ ReadColor(m_colorBackground, pColors, "Background");
+ ReadColor(m_colorSeparator, pColors, "Separator");
+ ReadColor(m_colorCursor, pColors, "Cursor");
+ ReadColor(m_colorNormal, pColors, "Normal");
+ ReadColor(m_colorSelected, pColors, "Selected");
+ ReadColor(m_colorHasNote, pColors, "HasNote");
+ ReadColor(m_colorHasSurrogateNote, pColors, "HasSurrogateNote");
+ ReadColor(m_colorHasBookmark, pColors, "HasBookmark");
+ ReadColor(m_colorFrozen, pColors, "Frozen");
+ ReadColor(m_colorHeader, pColors, "Header");
+ ReadColor(m_colorHeaderSelected, pColors, "HeaderSelected");
}
}
- if (document.HasMember("CodeNotes"))
+ ra::util::Json::Reader::Node pCodeNotes;
+ if (pJson.TryGetObject("CodeNotes", pCodeNotes))
{
- const rapidjson::Value& codeNotes = document["CodeNotes"];
-
- if (codeNotes.HasMember("Colors"))
+ ra::util::Json::Reader::Node pColors;
+ if (pCodeNotes.TryGetObject("Colors", pColors))
{
- const rapidjson::Value& colors = codeNotes["Colors"];
-
- ReadColor(m_colorNoteNormal, colors, "Normal");
- ReadColor(m_colorNoteModified, colors, "Modified");
+ ReadColor(m_colorNoteNormal, pColors, "Normal");
+ ReadColor(m_colorNoteModified, pColors, "Modified");
}
}
- if (document.HasMember("TriggerColors"))
+ ra::util::Json::Reader::Node pTriggerColors;
+ if (pJson.TryGetObject("TriggerColors", pTriggerColors))
{
- const rapidjson::Value& triggerColors = document["TriggerColors"];
- ReadColor(m_colorTriggerIsTrue, triggerColors, "IsTrue");
- ReadColor(m_colorTriggerWasTrue, triggerColors, "WasTrue");
- ReadColor(m_colorTriggerBecomingTrue, triggerColors, "BecomingTrue");
- ReadColor(m_colorTriggerResetTrue, triggerColors, "ResetTrue");
- ReadColor(m_colorTriggerPauseTrue, triggerColors, "PauseTrue");
+ ReadColor(m_colorTriggerIsTrue, pTriggerColors, "IsTrue");
+ ReadColor(m_colorTriggerWasTrue, pTriggerColors, "WasTrue");
+ ReadColor(m_colorTriggerBecomingTrue, pTriggerColors, "BecomingTrue");
+ ReadColor(m_colorTriggerResetTrue, pTriggerColors, "ResetTrue");
+ ReadColor(m_colorTriggerPauseTrue, pTriggerColors, "PauseTrue");
}
}
diff --git a/src/ui/viewmodels/MemoryBookmarksViewModel.cpp b/src/ui/viewmodels/MemoryBookmarksViewModel.cpp
index 0c9ad814..3f2eaab0 100644
--- a/src/ui/viewmodels/MemoryBookmarksViewModel.cpp
+++ b/src/ui/viewmodels/MemoryBookmarksViewModel.cpp
@@ -1,7 +1,7 @@
#include "MemoryBookmarksViewModel.hh"
#include "RA_Defs.h"
-#include "RA_Json.h"
+#include "util\Json.hh"
#include "util\Strings.hh"
#include "context\IEmulatorMemoryContext.hh"
@@ -197,13 +197,13 @@ void MemoryBookmarksViewModel::LoadBookmarks(ra::services::TextReader& sBookmark
m_vmMemoryWatchList.Items().BeginUpdate();
- rapidjson::Document document;
- if (LoadDocument(document, sBookmarksFile))
+ ra::util::Json::Reader pJson;
+ if (pJson.Parse(sBookmarksFile))
{
- if (document.HasMember("Bookmarks"))
+ std::vector vBookmarks;
+ if (pJson.TryGetObjectArray("Bookmarks", vBookmarks))
{
- const auto& bookmarks = document["Bookmarks"];
- for (const auto& bookmark : bookmarks.GetArray())
+ for (const auto& bookmark : vBookmarks)
{
auto* vmBookmark = m_vmMemoryWatchList.Items().GetItemAt(nIndex);
if (vmBookmark == nullptr)
@@ -215,28 +215,26 @@ void MemoryBookmarksViewModel::LoadBookmarks(ra::services::TextReader& sBookmark
vmBookmark->BeginInitialization();
- if (bookmark.HasMember("MemAddr"))
+ std::string sMemAddr;
+ if (bookmark.TryGetString("MemAddr", sMemAddr))
{
// third bookmark format uses the memref serializer
- const char* memaddr = bookmark["MemAddr"].GetString();
- InitializeBookmark(*vmBookmark, memaddr);
+ InitializeBookmark(*vmBookmark, sMemAddr);
- if (bookmark.HasMember("Size"))
+ switch (bookmark.GetInteger("Size"))
{
- switch (bookmark["Size"].GetInt())
- {
- case 15: vmBookmark->SetSize(ra::data::Memory::Size::Text); break;
- }
+ case 15: vmBookmark->SetSize(ra::data::Memory::Size::Text); break;
}
}
else
{
auto nSize = ra::data::Memory::Size::EightBit;
- if (bookmark.HasMember("Type"))
+ int nType;
+ if (bookmark.TryGetInteger("Type", nType))
{
// original bookmark format used Type for the three supported sizes.
- switch (bookmark["Type"].GetInt())
+ switch (nType)
{
case 1: nSize = ra::data::Memory::Size::EightBit; break;
case 2: nSize = ra::data::Memory::Size::SixteenBit; break;
@@ -247,7 +245,8 @@ void MemoryBookmarksViewModel::LoadBookmarks(ra::services::TextReader& sBookmark
{
// second bookmark format used the raw enum values, which was fragile.
// this enumerates the mapping for backwards compatibility.
- switch (bookmark["Size"].GetInt())
+ nType = bookmark.GetInteger("Size", 10);
+ switch (nType)
{
case 0: nSize = ra::data::Memory::Size::Bit0; break;
case 1: nSize = ra::data::Memory::Size::Bit1; break;
@@ -269,10 +268,10 @@ void MemoryBookmarksViewModel::LoadBookmarks(ra::services::TextReader& sBookmark
}
vmBookmark->SetSize(nSize);
- vmBookmark->SetAddress(bookmark["Address"].GetUint());
+ vmBookmark->SetAddress(bookmark.GetInteger("Address"));
}
- if (bookmark.HasMember("Decimal") && bookmark["Decimal"].GetBool())
+ if (bookmark.GetBoolean("Decimal", false))
vmBookmark->SetFormat(ra::data::Memory::Format::Dec);
else
vmBookmark->SetFormat(ra::data::Memory::Format::Hex);
@@ -280,10 +279,11 @@ void MemoryBookmarksViewModel::LoadBookmarks(ra::services::TextReader& sBookmark
if (!vmBookmark->IsIndirectAddress()) // Indirect note already called UpdateRealNote
vmBookmark->UpdateRealNote();
- if (bookmark.HasMember("Description"))
+ std::string sDescription;
+ if (bookmark.TryGetString("Description", sDescription))
{
- const auto sDescription = ra::util::String::Widen(bookmark["Description"].GetString());
- vmBookmark->SetDescription(sDescription);
+ const auto sDescriptionWide = ra::util::String::Widen(sDescription);
+ vmBookmark->SetDescription(sDescriptionWide);
}
vmBookmark->SetBehavior(MemoryBookmarksViewModel::BookmarkBehavior::None);
@@ -305,53 +305,49 @@ void MemoryBookmarksViewModel::SaveBookmarks(ra::services::TextWriter& sBookmark
{
std::string sSerialized;
- rapidjson::Document document;
- auto& allocator = document.GetAllocator();
- document.SetObject();
+ ra::util::Json::Writer pWriter;
+ ra::util::Json::Writer::Node pBookmarks = pWriter.SetObjectArray("Bookmarks");
- rapidjson::Value bookmarks(rapidjson::kArrayType);
for (auto& vmBookmark : m_vmMemoryWatchList.Items())
{
- rapidjson::Value item(rapidjson::kObjectType);
+ ra::util::Json::Writer::Node pBookmark = pBookmarks.AppendObject();
const auto nSize = vmBookmark.GetSize();
switch (nSize)
{
case ra::data::Memory::Size::Text:
- item.AddMember("Size", 15, allocator);
+ pBookmark.SetInteger("Size", 15);
if (vmBookmark.IsIndirectAddress())
- item.AddMember("MemAddr", vmBookmark.GetIndirectAddress(), allocator);
+ pBookmark.SetString("MemAddr", vmBookmark.GetIndirectAddress());
else
- item.AddMember("Address", vmBookmark.GetAddress(), allocator);
+ pBookmark.SetInteger("Address", vmBookmark.GetAddress());
break;
default:
if (vmBookmark.IsIndirectAddress())
{
- item.AddMember("MemAddr", vmBookmark.GetIndirectAddress(), allocator);
+ pBookmark.SetString("MemAddr", vmBookmark.GetIndirectAddress());
}
else
{
sSerialized.clear();
ra::services::AchievementLogicSerializer::AppendOperand(
sSerialized, ra::services::TriggerOperandType::Address, nSize, vmBookmark.GetAddress());
- item.AddMember("MemAddr", sSerialized, allocator);
+ pBookmark.SetString("MemAddr", sSerialized);
}
break;
}
if (vmBookmark.GetFormat() != ra::data::Memory::Format::Hex)
- item.AddMember("Decimal", true, allocator);
+ pBookmark.SetBoolean("Decimal", true);
if (vmBookmark.IsCustomDescription())
- item.AddMember("Description", ra::util::String::Narrow(vmBookmark.GetDescription()), allocator);
+ pBookmark.SetString("Description", ra::util::String::Narrow(vmBookmark.GetDescription()));
- bookmarks.PushBack(item, allocator);
vmBookmark.ResetModified();
}
- document.AddMember("Bookmarks", bookmarks, allocator);
- SaveDocument(document, sBookmarksFile);
+ pWriter.Save(sBookmarksFile);
}
void MemoryBookmarksViewModel::DoFrame()
diff --git a/src/ui/viewmodels/MemoryWatchListViewModel.cpp b/src/ui/viewmodels/MemoryWatchListViewModel.cpp
index c7566bd1..9d81b12e 100644
--- a/src/ui/viewmodels/MemoryWatchListViewModel.cpp
+++ b/src/ui/viewmodels/MemoryWatchListViewModel.cpp
@@ -1,7 +1,6 @@
#include "MemoryWatchListViewModel.hh"
#include "RA_Defs.h"
-#include "RA_Json.h"
#include "util\Strings.hh"
#include "data\Types.hh"
diff --git a/src/ui/viewmodels/MemoryWatchViewModel.cpp b/src/ui/viewmodels/MemoryWatchViewModel.cpp
index 34d8c38c..6043de78 100644
--- a/src/ui/viewmodels/MemoryWatchViewModel.cpp
+++ b/src/ui/viewmodels/MemoryWatchViewModel.cpp
@@ -1,7 +1,6 @@
#include "MemoryWatchViewModel.hh"
#include "RA_Defs.h"
-#include "RA_Json.h"
#include "util\Strings.hh"
#include "context\IConsoleContext.hh"
diff --git a/tests/RA_Integration.Tests.vcxproj b/tests/RA_Integration.Tests.vcxproj
index 004d93a0..7ccfb6ea 100644
--- a/tests/RA_Integration.Tests.vcxproj
+++ b/tests/RA_Integration.Tests.vcxproj
@@ -83,7 +83,6 @@
Create
-
diff --git a/tests/RA_Integration.Tests.vcxproj.filters b/tests/RA_Integration.Tests.vcxproj.filters
index 00c5b4ae..064e9736 100644
--- a/tests/RA_Integration.Tests.vcxproj.filters
+++ b/tests/RA_Integration.Tests.vcxproj.filters
@@ -63,9 +63,6 @@
Code
-
- Code
-
Tests\Services