From 6fc24d8883b925a89dc80683b73284ed1ee5af5f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Feb 2021 15:50:00 -0800 Subject: [PATCH] htc: implement service channel parsing (ReceiveReadyPacket) --- .../source/htclow/ctrl/htclow_json.cpp | 80 +++++++++ .../source/htclow/ctrl/htclow_json.hpp | 50 ++++++ .../ctrl/htclow_service_channel_parser.cpp | 156 ++++++++++++++++++ .../ctrl/htclow_service_channel_parser.hpp | 23 +++ .../include/vapours/util/util_string_util.hpp | 13 ++ 5 files changed, 322 insertions(+) create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_json.cpp create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_json.hpp create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.cpp create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.hpp diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_json.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_json.cpp new file mode 100644 index 000000000..df248060c --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_json.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_json.hpp" + +namespace ams::htclow::ctrl { + + namespace { + + constexpr const char ChannelKey[] = "Chan"; + constexpr const char ProtocolVersionKey[] = "Prot"; + + } + + bool JsonHandler::Key(const Ch *str, rapidjson::SizeType len, bool copy) { + if (m_state == State::ParseObject) { + if (!util::Strncmp(str, ChannelKey, sizeof(ChannelKey))) { + m_state = State::ParseServiceChannels; + } + if (!util::Strncmp(str, ProtocolVersionKey, sizeof(ProtocolVersionKey))) { + m_state = State::ParseProtocolVersion; + } + } + return true; + } + + bool JsonHandler::Uint(unsigned val) { + if (m_state == State::ParseProtocolVersion) { + *m_version = val; + } + return true; + } + + bool JsonHandler::String(const Ch *str, rapidjson::SizeType len, bool copy) { + if (m_state == State::ParseServiceChannelsArray && *m_num_strings < m_max_strings) { + m_strings[(*m_num_strings)++] = str; + } + return true; + } + + bool JsonHandler::StartObject() { + if (m_state == State::Begin) { + m_state = State::ParseObject; + } + return true; + } + + bool JsonHandler::EndObject(rapidjson::SizeType) { + m_state = State::End; + return true; + } + + bool JsonHandler::StartArray() { + if (m_state == State::ParseServiceChannels) { + m_state = State::ParseServiceChannelsArray; + } + return true; + } + + bool JsonHandler::EndArray(rapidjson::SizeType len) { + if (m_state == State::ParseServiceChannelsArray && len) { + m_state = State::ParseObject; + } + return true; + } + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_json.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_json.hpp new file mode 100644 index 000000000..12dc23c4a --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_json.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow::ctrl { + + class JsonHandler : public rapidjson::BaseReaderHandler, JsonHandler>{ + private: + enum class State { + Begin = 0, + ParseObject = 1, + ParseServiceChannels = 2, + ParseServiceChannelsArray = 3, + ParseProtocolVersion = 4, + End = 5, + }; + private: + State m_state; + s16 *m_version; + const char **m_strings; + int *m_num_strings; + int m_max_strings; + public: + JsonHandler(s16 *vers, const char **str, int *ns, int max) : m_state(State::Begin), m_version(vers), m_strings(str), m_num_strings(ns), m_max_strings(max) { /* ... */ } + + bool Key(const Ch *str, rapidjson::SizeType len, bool copy); + bool Uint(unsigned); + bool String(const Ch *, rapidjson::SizeType, bool); + + bool StartObject(); + bool EndObject(rapidjson::SizeType); + bool StartArray(); + bool EndArray(rapidjson::SizeType); + }; + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.cpp new file mode 100644 index 000000000..2a64382ce --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_json.hpp" +#include "htclow_service_channel_parser.hpp" + +namespace ams::htclow::ctrl { + + namespace { + + void ParseBody(s16 *out_version, const char **out_channels, int *out_num_channels, int max_channels, void *str, size_t str_size) { + /* Create JSON handler. */ + JsonHandler json_handler(out_version, out_channels, out_num_channels, max_channels); + + /* Create reader. */ + rapidjson::Reader json_reader; + + /* Create stream. */ + rapidjson::InsituStringStream json_stream(static_cast(str)); + + /* Parse the json. */ + json_reader.Parse(json_stream, json_handler); + } + + constexpr bool IsNumericCharacter(char c) { + return '0' <= c && c <= '9'; + } + + constexpr bool IsValidCharacter(char c) { + return IsNumericCharacter(c) || c == ':'; + } + + bool IntegerToModuleId(ModuleId *out, int id) { + switch (id) { + case 0: + case 1: + case 3: + case 4: + *out = static_cast(id); + return true; + default: + return false; + } + } + + bool StringToChannel(impl::ChannelInternalType *out, char *str, size_t size) { + enum class State { + Begin, + ModuleId, + Sep1, + Reserved, + Sep2, + ChannelId + }; + + State state = State::Begin; + + const char * cur = nullptr; + + const char * const str_end = str + size; + while (str < str_end && IsValidCharacter(*str)) { + const char c = *str; + + switch (state) { + case State::Begin: + if (IsNumericCharacter(c)) { + cur = str; + state = State::ModuleId; + } + break; + case State::ModuleId: + if (c == ':') { + *str = 0; + if (!IntegerToModuleId(std::addressof(out->module_id), atoi(cur))) { + return false; + } + state = State::Sep1; + } else if (!IsNumericCharacter(c)) { + return false; + } + break; + case State::Sep1: + if (IsNumericCharacter(c)) { + cur = str; + state = State::Reserved; + } + break; + case State::Reserved: + if (c == ':') { + *str = 0; + out->reserved = 0; + state = State::Sep2; + } else if (!IsNumericCharacter(c)) { + return false; + } + break; + case State::Sep2: + if (IsNumericCharacter(c)) { + cur = str; + state = State::ChannelId; + } + break; + case State::ChannelId: + if (!IsNumericCharacter(c)) { + return false; + } + break; + } + + ++str; + } + + if (str != str_end) { + return false; + } + + out->channel_id = atoi(cur); + + return true; + } + + } + + void ParseServiceChannel(s16 *out_version, impl::ChannelInternalType *out_channels, int *out_num_channels, int max_channels, void *str, size_t str_size) { + /* Parse the JSON. */ + const char *channel_strs[0x20]; + int num_channels; + ParseBody(out_version, channel_strs, std::addressof(num_channels), util::size(channel_strs), str, str_size); + + /* Parse the channel strings. */ + char * const str_end = static_cast(str) + str_size; + int parsed_channels = 0; + for (auto i = 0; i < num_channels && i < max_channels; ++i) { + impl::ChannelInternalType channel; + if (StringToChannel(std::addressof(channel), const_cast(channel_strs[i]), util::Strnlen(channel_strs[i], str_end - channel_strs[i]))) { + out_channels[parsed_channels++] = channel; + } + } + + *out_num_channels = parsed_channels; + } + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.hpp new file mode 100644 index 000000000..1b0c1ec8e --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow::ctrl { + + void ParseServiceChannel(s16 *out_version, impl::ChannelInternalType *out_channels, int *out_num_channels, int max_channels, void *str, size_t str_size); + +} diff --git a/libraries/libvapours/include/vapours/util/util_string_util.hpp b/libraries/libvapours/include/vapours/util/util_string_util.hpp index 9abe8aaab..a70f0cae4 100644 --- a/libraries/libvapours/include/vapours/util/util_string_util.hpp +++ b/libraries/libvapours/include/vapours/util/util_string_util.hpp @@ -88,4 +88,17 @@ namespace ams::util { return static_cast(cur - src); } + template + constexpr int Strnlen(const T *str, int count) { + AMS_ASSERT(str != nullptr); + AMS_ASSERT(count >= 0); + + int length = 0; + while (count-- && *str++) { + ++length; + } + + return length; + } + }