From e16bbfda12f7499a7a60ae75666aa138ffbb84ed Mon Sep 17 00:00:00 2001 From: SMJSGaming Date: Tue, 2 Jul 2024 20:07:27 +0200 Subject: [PATCH] Added API support --- TODO | 5 +- {src/lib => lib}/json.hpp | 0 mod.json | 5 + proxy/HttpInfo.hpp | 87 +++++++ proxy/Proxy.hpp | 66 +++++ proxy/converters/BinaryToRaw.hpp | 11 + proxy/converters/Converter.hpp | 23 ++ proxy/converters/FormToJson.hpp | 10 + proxy/converters/RobTopToJson.hpp | 49 ++++ src/include.cpp | 13 + src/include.hpp | 12 +- src/main.cpp | 37 ++- src/mods/CCHttpClient.cpp | 12 +- src/mods/CCHttpClient.hpp | 2 +- src/mods/GeodeWeb.cpp | 17 ++ src/mods/MenuLayer.hpp | 1 - src/nodes/CodeBlock.cpp | 8 +- src/nodes/CodeBlock.hpp | 1 - src/nodes/ControlMenu.cpp | 74 ++++++ src/nodes/ControlMenu.hpp | 17 ++ src/nodes/InfoArea.cpp | 10 +- src/nodes/InfoArea.hpp | 1 - src/nodes/cells/CaptureCell.cpp | 18 +- src/nodes/cells/CaptureCell.hpp | 2 +- src/nodes/cells/CodeLineCell.hpp | 1 - src/nodes/lists/CaptureList.cpp | 12 +- src/nodes/lists/CaptureList.hpp | 6 +- src/nodes/lists/JSONCodeBlock.cpp | 2 +- src/nodes/lists/JSONCodeBlock.hpp | 2 - src/nodes/lists/TouchFixList.cpp | 37 --- src/nodes/lists/TouchFixList.hpp | 9 - src/objects/HttpInfo.cpp | 234 ------------------ src/objects/HttpInfo.hpp | 74 ------ src/objects/converters/BinaryToRaw.hpp | 9 - src/objects/converters/FormToJson.hpp | 8 - src/objects/converters/JsonConverter.hpp | 16 -- src/objects/converters/RobTopToJson.hpp | 47 ---- src/proxy/HttpInfo.cpp | 199 +++++++++++++++ src/proxy/Proxy.cpp | 23 ++ src/proxy/ProxyHandler.cpp | 50 ++++ src/proxy/ProxyHandler.hpp | 22 ++ .../converters/BinaryToRaw.cpp | 6 +- .../converters/Converter.cpp} | 36 +-- .../converters/FormToJson.cpp | 8 +- .../converters/RobTopToJson.cpp | 34 +-- src/scenes/InterceptPopup.cpp | 45 ++-- src/scenes/InterceptPopup.hpp | 7 +- 47 files changed, 821 insertions(+), 547 deletions(-) rename {src/lib => lib}/json.hpp (100%) create mode 100644 proxy/HttpInfo.hpp create mode 100644 proxy/Proxy.hpp create mode 100644 proxy/converters/BinaryToRaw.hpp create mode 100644 proxy/converters/Converter.hpp create mode 100644 proxy/converters/FormToJson.hpp create mode 100644 proxy/converters/RobTopToJson.hpp create mode 100644 src/include.cpp create mode 100644 src/mods/GeodeWeb.cpp create mode 100644 src/nodes/ControlMenu.cpp create mode 100644 src/nodes/ControlMenu.hpp delete mode 100644 src/nodes/lists/TouchFixList.cpp delete mode 100644 src/nodes/lists/TouchFixList.hpp delete mode 100644 src/objects/HttpInfo.cpp delete mode 100644 src/objects/HttpInfo.hpp delete mode 100644 src/objects/converters/BinaryToRaw.hpp delete mode 100644 src/objects/converters/FormToJson.hpp delete mode 100644 src/objects/converters/JsonConverter.hpp delete mode 100644 src/objects/converters/RobTopToJson.hpp create mode 100644 src/proxy/HttpInfo.cpp create mode 100644 src/proxy/Proxy.cpp create mode 100644 src/proxy/ProxyHandler.cpp create mode 100644 src/proxy/ProxyHandler.hpp rename src/{objects => proxy}/converters/BinaryToRaw.cpp (66%) rename src/{objects/converters/JsonConverter.cpp => proxy/converters/Converter.cpp} (68%) rename src/{objects => proxy}/converters/FormToJson.cpp (68%) rename src/{objects => proxy}/converters/RobTopToJson.cpp (77%) diff --git a/TODO b/TODO index 08f4a6e..55ce13c 100644 --- a/TODO +++ b/TODO @@ -18,10 +18,11 @@ Alpha 3 Todo: ☐ @high Add a send button ☐ @critical Add a pause/resume button ☐ @high Add more keybinds + ✔ @low Add modding API support @done(24-07-02 15:10) + ☐ @high Add mod request support Release Todo: ☐ Add inputs to the json code block - # TODO: Figure out wtf I meant by this - ☐ Add a format to original in the converters + ☐ Add a formatted text to original in the converters Nice To Haves: ☐ Custom theme support ☐ Do some lobbying to push the Geode team to add a request event diff --git a/src/lib/json.hpp b/lib/json.hpp similarity index 100% rename from src/lib/json.hpp rename to lib/json.hpp diff --git a/mod.json b/mod.json index 67a0893..c147301 100644 --- a/mod.json +++ b/mod.json @@ -3,6 +3,11 @@ "version": "v0.2.3-alpha.2", "id": "smjs.gdintercept", "name": "GDIntercept", + "api": { + "include": [ + "include/*.hpp" + ] + }, "developers": [ "SMJSGaming", "RobTop" diff --git a/proxy/HttpInfo.hpp b/proxy/HttpInfo.hpp new file mode 100644 index 0000000..0e93e52 --- /dev/null +++ b/proxy/HttpInfo.hpp @@ -0,0 +1,87 @@ +#include +#include +#include "../lib/json.hpp" +#include "converters/Converter.hpp" +#include "converters/FormToJson.hpp" +#include "converters/BinaryToRaw.hpp" +#include "converters/RobTopToJson.hpp" + +namespace proxy { + #define GETTER(type, name, capital_name) \ + public: \ + type get##capital_name() const { return m_##name; } \ + private: \ + type m_##name; + + using namespace geode::prelude; + using namespace nlohmann; + + struct HttpInfo { + enum Origin { + GD, + GD_CDN, + ROBTOP_GAMES, + NEWGROUNDS, + GEODE, + OTHER + }; + + enum Protocol { + HTTP, + HTTPS, + UNKNOWN_PROTOCOL + }; + + enum ContentType { + FORM, + JSON, + XML, + ROBTOP, + BINARY, + UNKNOWN_CONTENT + }; + + typedef std::pair content; + + std::string stringifyProtocol() const; + std::string stringifyMethod() const; + std::string stringifyQuery() const; + std::string stringifyHeaders() const; + content getBodyContent(const bool raw = true); + content getResponseContent(const bool raw = true); + void resetCache(); + private: + static converters::FormToJson formToJson; + static converters::RobTopToJson robtopToJson; + static converters::BinaryToRaw binaryToRaw; + + GETTER(Origin, origin, Origin) + GETTER(Protocol, protocol, Protocol) + GETTER(CCHttpRequest::HttpRequestType, method, Method) + GETTER(std::string, url, Url) + GETTER(std::string, host, Host) + GETTER(std::string, path, Path) + GETTER(json, query, Query) + GETTER(json, headers, Headers) + GETTER(std::string, body, Body) + GETTER(ContentType, bodyContentType, BodyContentType) + GETTER(int, statusCode, StatusCode) + GETTER(std::string, response, Response) + GETTER(ContentType, responseContentType, ResponseContentType) + content m_simplifiedBodyCache; + content m_simplifiedResponseCache; + + HttpInfo(CCHttpRequest* request); + HttpInfo(web::WebRequest* request, const std::string& method, const std::string& url); + content getContent(const bool raw, const ContentType originalContentType, const std::string& original, content& cache); + content simplifyContent(const content& content); + ContentType determineContentType(const std::string& content, const bool isBody = false); + bool isDomain(const std::string& domain); + void determineOrigin(); + void parseUrl(const std::string& url); + + friend struct ProxyHandler; + }; + + #undef GETTER +} \ No newline at end of file diff --git a/proxy/Proxy.hpp b/proxy/Proxy.hpp new file mode 100644 index 0000000..cea39b5 --- /dev/null +++ b/proxy/Proxy.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include +#include "HttpInfo.hpp" + +namespace proxy { + using namespace geode::prelude; + + enum SourceFilter { + GD, + GD_CDN, + ROBTOP_GAMES, + NEWGROUNDS, + GEODE, + OTHER, + ALL + }; + + struct ProxyEvent : public Event { + ProxyEvent(HttpInfo* info); + HttpInfo* getRequest() const; + protected: + HttpInfo* m_info; + }; + + struct RequestEvent : public ProxyEvent { + RequestEvent(HttpInfo* info); + }; + + struct ResponseEvent : public ProxyEvent { + ResponseEvent(HttpInfo* info); + HttpInfo::content getResponse(const bool raw = true) const; + }; + + template + concept proxy_event = std::is_base_of_v; + + template + struct ProxyFilter : public EventFilter { + ProxyFilter(const SourceFilter source = ALL) : m_source(source) { }; + ProxyFilter(const SourceFilter source, const std::initializer_list& urls) : m_source(source), m_urls(urls) { }; + ListenerResult handle(MiniFunction callback, T* event) { + if ( + (m_urls.empty() || std::find(m_urls.begin(), m_urls.end(), event->getRequest()->getUrl()) != m_urls.end()) && + (m_source == ALL || as(m_source) == as(event->getRequest()->getOrigin())) + ) { + return callback(event); + } else { + return ListenerResult::Propagate; + } + } + protected: + SourceFilter m_source; + std::vector m_urls; + }; + + struct RequestFilter : public ProxyFilter { + RequestFilter(const SourceFilter source = ALL); + RequestFilter(const SourceFilter source, const std::initializer_list& urls); + }; + + struct ResponseFilter : public ProxyFilter { + ResponseFilter(const SourceFilter source = ALL); + ResponseFilter(const SourceFilter source, const std::initializer_list& urls); + }; +} \ No newline at end of file diff --git a/proxy/converters/BinaryToRaw.hpp b/proxy/converters/BinaryToRaw.hpp new file mode 100644 index 0000000..cd43e1b --- /dev/null +++ b/proxy/converters/BinaryToRaw.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include "Converter.hpp" + +namespace proxy::converters { + // What can I say? I like it raw + struct BinaryToRaw : public Converter { + bool canConvert(const std::string& path, const std::string& original) override; + std::string convert(const std::string& path, const std::string& original) override; + }; +} \ No newline at end of file diff --git a/proxy/converters/Converter.hpp b/proxy/converters/Converter.hpp new file mode 100644 index 0000000..ec71fc9 --- /dev/null +++ b/proxy/converters/Converter.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include "../../lib/json.hpp" + +namespace proxy::converters { + using namespace geode::prelude; + using namespace nlohmann; + + bool isInt(const std::string& str); + bool isNumber(const std::string& str); + bool isBool(const std::string& str); + bool isNull(const std::string& str); + bool isString(const std::string& str); + bool isJson(const std::string& str); + json getPrimitiveJsonType(const std::string& key, const std::string& str); + + template + struct Converter { + virtual bool canConvert(const std::string& path, const std::string& original) = 0; + virtual T convert(const std::string& path, const std::string& original) = 0; + }; +} \ No newline at end of file diff --git a/proxy/converters/FormToJson.hpp b/proxy/converters/FormToJson.hpp new file mode 100644 index 0000000..0a9f959 --- /dev/null +++ b/proxy/converters/FormToJson.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include "Converter.hpp" + +namespace proxy::converters { + struct FormToJson : public Converter { + bool canConvert(const std::string& path, const std::string& original) override; + json convert(const std::string& path, const std::string& original) override; + }; +} \ No newline at end of file diff --git a/proxy/converters/RobTopToJson.hpp b/proxy/converters/RobTopToJson.hpp new file mode 100644 index 0000000..7dde4a1 --- /dev/null +++ b/proxy/converters/RobTopToJson.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include "Converter.hpp" + +namespace proxy::converters { + struct RobTopToJson : public Converter { + bool canConvert(const std::string& path, const std::string& original) override; + json convert(const std::string& path, const std::string& original) override; + private: + enum ObjectType { + OBJECT, + ARRAY + }; + + enum SeparatorType { + KEY_VALUE, + TUPLE + }; + + // The only reason why const char* is involved here is because std::string basically sees everything as a string ctor so overloading with it was almost impossible + struct ObjParser { + ObjParser(const char* delimiter, const char* entryDelimiter = ""); + ObjParser(const std::vector& tupleKeys, const char* delimiter = "", const char* entryDelimiter = ""); + virtual json parse(const std::string& str) const; + private: + ObjectType m_bodyType; + SeparatorType m_separatorType; + std::string m_delimiter; + std::string m_entryDelimiter; + std::vector m_tupleKeys; + + static std::vector split(const std::string& str, const std::string& delimiter); + + json parseEntry(const std::string& str) const; + }; + + struct Parser : public ObjParser { + Parser(const char* delimiter, const std::vector>& metadataKeys = {}); + Parser(const std::vector& tupleKeys, const char* delimiter = "", const std::vector>& metadataKeys = {}); + Parser(const char* delimiter, const char* entryDelimiter, const std::vector>& metadataKeys = {}); + Parser(const std::vector& tupleKeys, const char* delimiter, const char* entryDelimiter, const std::vector>& metadataKeys = {}); + json parse(const std::string& str) const override; + private: + std::vector> m_metadata; + }; + + static const std::unordered_map parsers; + }; +} \ No newline at end of file diff --git a/src/include.cpp b/src/include.cpp new file mode 100644 index 0000000..fd2bd80 --- /dev/null +++ b/src/include.cpp @@ -0,0 +1,13 @@ +#include "include.hpp" + +std::vector context::CACHED_PROXIES; + +void context::registerRequest(ProxyHandler* proxy) { + if (!Mod::get()->getSettingValue("remember-requests") && CACHED_PROXIES.size() > 0) { + delete CACHED_PROXIES.at(0); + + CACHED_PROXIES.resize(0); + } + + CACHED_PROXIES.insert(CACHED_PROXIES.begin(), proxy); +} \ No newline at end of file diff --git a/src/include.hpp b/src/include.hpp index 67a5ca8..8f95321 100644 --- a/src/include.hpp +++ b/src/include.hpp @@ -1,10 +1,13 @@ #pragma once #include -#include "lib/json.hpp" +#include "../lib/json.hpp" +#include "../proxy/Proxy.hpp" +#include "proxy/ProxyHandler.hpp" -using namespace geode::prelude; +using namespace proxy; using namespace nlohmann; +using namespace geode::prelude; #define PADDING 5.0f #define FULL_OPACITY 0xFF @@ -30,4 +33,9 @@ constexpr ccColor4B BROWN_4B({ 0xA0, 0x54, 0x34, FULL_OPACITY }); constexpr ccColor3B DARK_BROWN_3B({ 0x82, 0x40, 0x21 }); constexpr ccColor4B DARK_BROWN_4B({ 0x82, 0x40, 0x21, FULL_OPACITY }); +namespace context { + extern std::vector CACHED_PROXIES; + void registerRequest(ProxyHandler* proxy); +} + #define OPT(expr) if (auto _opt_ = expr) _opt_ \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 99ea0a7..703fd60 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,5 @@ #include #include "include.hpp" -#include "objects/HttpInfo.hpp" #include "scenes/InterceptPopup.hpp" #ifdef GEODE_IS_WINDOWS @@ -49,14 +48,32 @@ #endif $execute { + new EventListener([=](RequestEvent* event) { + if (Mod::get()->getSettingValue("log-requests")) { + HttpInfo* request = event->getRequest(); + + log::info("Sending request:\nMethod: {}\nProtocol: {}\nHost: {}\nPath: {}\nQuery: {}\nHeaders: {}\nBody: {}", + request->stringifyMethod(), + request->stringifyProtocol(), + request->getHost(), + request->getPath(), + request->stringifyQuery(), + request->stringifyHeaders(), + request->getBodyContent(false).second + ); + } + + return ListenerResult::Propagate; + }, RequestFilter()); + listenForSettingChanges("remember-requests", +[](const bool value) { if (!value) { - for (size_t i = 1; i < HttpInfo::requests.size(); i++) { - delete HttpInfo::requests.at(i); + for (size_t i = 1; i < context::CACHED_PROXIES.size(); i++) { + delete context::CACHED_PROXIES.at(i); } - if (HttpInfo::requests.size() > 1) { - HttpInfo::requests.resize(1); + if (context::CACHED_PROXIES.size() > 1) { + context::CACHED_PROXIES.resize(1); } OPT(InterceptPopup::get())->reload(); @@ -65,13 +82,17 @@ listenForSettingChanges("cache", +[](const bool value) { if (!value) { - for (HttpInfo* request : HttpInfo::requests) { - request->resetCache(); + for (ProxyHandler* proxy : context::CACHED_PROXIES) { + proxy->getInfo()->resetCache(); } } }); listenForAllSettingChanges(+[](SettingValue* event) { - OPT(InterceptPopup::get())->reload(); + std::vector blacklist({ "remember-requests", "cache" }); + + if (std::find(blacklist.begin(), blacklist.end(), event->getKey()) == blacklist.end()) { + OPT(InterceptPopup::get())->reload(); + } }); } \ No newline at end of file diff --git a/src/mods/CCHttpClient.cpp b/src/mods/CCHttpClient.cpp index b8b7362..4e37370 100644 --- a/src/mods/CCHttpClient.cpp +++ b/src/mods/CCHttpClient.cpp @@ -1,16 +1,8 @@ #include "CCHttpClient.hpp" void ModCCHttpClient::send(CCHttpRequest* request) { - HttpInfo* requestInfo = HttpInfo::create(request); - - if (Mod::get()->getSettingValue("log-requests")) { - log::info("Sending request:\n{}\nQuery: {}\nHeaders: {}\nBody: {}", - requestInfo->generateBasicInfo(false), - requestInfo->formatQuery(), - requestInfo->formatHeaders(), - requestInfo->formatBody().second - ); - } + ProxyHandler* proxy = ProxyHandler::create(request); + context::registerRequest(proxy); CCHttpClient::send(request); } \ No newline at end of file diff --git a/src/mods/CCHttpClient.hpp b/src/mods/CCHttpClient.hpp index 673bde7..80dfc55 100644 --- a/src/mods/CCHttpClient.hpp +++ b/src/mods/CCHttpClient.hpp @@ -2,7 +2,7 @@ #include #include "../include.hpp" -#include "../objects/HttpInfo.hpp" +#include "../proxy/ProxyHandler.hpp" class $modify(ModCCHttpClient, CCHttpClient) { static void onModify(auto& self) { diff --git a/src/mods/GeodeWeb.cpp b/src/mods/GeodeWeb.cpp new file mode 100644 index 0000000..a0739f9 --- /dev/null +++ b/src/mods/GeodeWeb.cpp @@ -0,0 +1,17 @@ +#include +#include "../include.hpp" + +web::WebTask WebRequest_send(web::WebRequest* request, std::string_view method, std::string_view url) { + log::info("{} {}", method, url); + + return request->send(method, url); +} + +$execute { + (void) Mod::get()->hook( + reinterpret_cast(addresser::getNonVirtual(&web::WebRequest::send)), + &WebRequest_send, + "geode::web::WebRequest::send", + tulip::hook::TulipConvention::Thiscall + ); +} \ No newline at end of file diff --git a/src/mods/MenuLayer.hpp b/src/mods/MenuLayer.hpp index 9196d5c..b9aa538 100644 --- a/src/mods/MenuLayer.hpp +++ b/src/mods/MenuLayer.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include "../include.hpp" #include "../scenes/InterceptPopup.hpp" diff --git a/src/nodes/CodeBlock.cpp b/src/nodes/CodeBlock.cpp index fb7c628..5bcf980 100644 --- a/src/nodes/CodeBlock.cpp +++ b/src/nodes/CodeBlock.cpp @@ -58,22 +58,22 @@ bool CodeBlock::init(const CCSize& size, const CCSize& buttonBarSize) { } void CodeBlock::onBody(CCObject* sender) { - this->setCode(m_request->formatBody()); + this->setCode(m_request->getBodyContent(Mod::get()->getSettingValue("raw-data"))); this->updateDataTypeColor('B'); } void CodeBlock::onQuery(CCObject* sender) { - this->setCode({ HttpInfo::JSON, m_request->formatQuery() }); + this->setCode({ HttpInfo::JSON, m_request->stringifyQuery() }); this->updateDataTypeColor('Q'); } void CodeBlock::onHeaders(CCObject* sender) { - this->setCode({ HttpInfo::JSON, m_request->formatHeaders() }); + this->setCode({ HttpInfo::JSON, m_request->stringifyQuery() }); this->updateDataTypeColor('H'); } void CodeBlock::onResponse(CCObject* sender) { - this->setCode(m_request->formatResponse()); + this->setCode(m_request->getResponseContent(Mod::get()->getSettingValue("raw-data"))); this->updateDataTypeColor('R'); } diff --git a/src/nodes/CodeBlock.hpp b/src/nodes/CodeBlock.hpp index 72bbd1d..7250807 100644 --- a/src/nodes/CodeBlock.hpp +++ b/src/nodes/CodeBlock.hpp @@ -2,7 +2,6 @@ #include "ButtonBar.hpp" #include "../include.hpp" -#include "../objects/HttpInfo.hpp" #include "lists/JSONCodeBlock.hpp" #include "../objects/ThemeStyle.hpp" diff --git a/src/nodes/ControlMenu.cpp b/src/nodes/ControlMenu.cpp new file mode 100644 index 0000000..ec2991b --- /dev/null +++ b/src/nodes/ControlMenu.cpp @@ -0,0 +1,74 @@ +#include "ControlMenu.hpp" + +ControlMenu* ControlMenu::create(const CCSize& size, FLAlertLayer*& settings) { + ControlMenu* instance = new ControlMenu(settings); + + if (instance && instance->init(size)) { + instance->autorelease(); + + return instance; + } else { + CC_SAFE_DELETE(instance); + + return nullptr; + } +} + +ControlMenu::ControlMenu(FLAlertLayer*& settings): m_settings(settings) { } + +bool ControlMenu::init(const CCSize& size) { + const CCSize menuSize(size - PADDING * 2); + const CCSize buttonSize(ccp(menuSize.width - PADDING * 6, (menuSize.height - PADDING * 3) / 2)); + CCScale9Sprite* infoBg = CCScale9Sprite::create("square02b_001.png"); + m_pauseButton = ButtonSprite::create( + Mod::get()->getSettingValue("pause-requests") ? "Resume" : "Pause", + buttonSize.width, + true, + "bigFont.fnt", + "GJ_button_01.png", + buttonSize.height, + 0.5f + ); + m_sendButton = ButtonSprite::create( + "Send", + buttonSize.width, + true, + "bigFont.fnt", + "GJ_button_01.png", + buttonSize.height, + 0.5f + ); + CCMenuItemSpriteExtra* pauseItem = CCMenuItemSpriteExtra::create(m_pauseButton, this, menu_selector(ControlMenu::onPause)); + CCMenuItemSpriteExtra* sendItem = CCMenuItemSpriteExtra::create(m_sendButton, this, menu_selector(ControlMenu::onSend)); + CCMenu* menu = CCMenu::create(); + + if (!Border::init(infoBg, LIGHTER_BROWN_4B, size)) { + return false; + } + + this->setPadding(PADDING); + infoBg->setColor(LIGHT_BROWN_3B); + infoBg->addChild(menu); + pauseItem->setPosition({ menuSize.width / 2, menuSize.height - buttonSize.height / 2 - PADDING }); + pauseItem->setSizeMult(pauseItem->m_scaleMultiplier = 1.1f); + sendItem->setPosition({ menuSize.width / 2, buttonSize.height / 2 + PADDING }); + sendItem->setSizeMult(sendItem->m_scaleMultiplier = 1.1f); + menu->setContentSize(menuSize); + menu->setPosition(ZERO_POINT); + menu->addChild(pauseItem); + menu->addChild(sendItem); + + return true; +} + +void ControlMenu::onPause(CCObject* sender) { + const bool value = !Mod::get()->getSettingValue("pause-requests"); + + m_settings = nullptr; + m_pauseButton->setString(value ? "Resume" : "Pause"); + Mod::get()->setSettingValue("pause-requests", value); +} + +void ControlMenu::onSend(CCObject* sender) { + // Mod::get()->sendRequest(); +} \ No newline at end of file diff --git a/src/nodes/ControlMenu.hpp b/src/nodes/ControlMenu.hpp new file mode 100644 index 0000000..941a9af --- /dev/null +++ b/src/nodes/ControlMenu.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "../include.hpp" + +struct ControlMenu : public Border { + // FYI: I don't like using *& here, geode please expose the settings node + static ControlMenu* create(const CCSize& size, FLAlertLayer*& settings); +private: + ButtonSprite* m_pauseButton; + ButtonSprite* m_sendButton; + FLAlertLayer*& m_settings; + + ControlMenu(FLAlertLayer*& settings); + bool init(const CCSize& size); + void onPause(CCObject* sender); + void onSend(CCObject* sender); +}; \ No newline at end of file diff --git a/src/nodes/InfoArea.cpp b/src/nodes/InfoArea.cpp index ead2aa5..18fca9b 100644 --- a/src/nodes/InfoArea.cpp +++ b/src/nodes/InfoArea.cpp @@ -36,5 +36,13 @@ bool InfoArea::init(const CCSize& size) { } void InfoArea::updateRequest(HttpInfo* request) { - as(this->getNode()->getChildByID("info_text"_spr))->setText(request->generateBasicInfo()); + const int statusCode = request->getStatusCode(); + + as(this->getNode()->getChildByID("info_text"_spr))->setText(fmt::format("Status Code: {}\nMethod: {}\nProtocol: {}\nHost: {}\nPath: {}", + statusCode == -1 ? "Request Error" : std::to_string(statusCode), + request->stringifyMethod(), + request->stringifyProtocol(), + request->getHost(), + request->getPath() + )); } \ No newline at end of file diff --git a/src/nodes/InfoArea.hpp b/src/nodes/InfoArea.hpp index 52b8576..eb3e6d1 100644 --- a/src/nodes/InfoArea.hpp +++ b/src/nodes/InfoArea.hpp @@ -1,7 +1,6 @@ #pragma once #include "../include.hpp" -#include "../objects/HttpInfo.hpp" struct InfoArea : public Border { static InfoArea* create(const CCSize& size); diff --git a/src/nodes/cells/CaptureCell.cpp b/src/nodes/cells/CaptureCell.cpp index cbf1370..f572fa5 100644 --- a/src/nodes/cells/CaptureCell.cpp +++ b/src/nodes/cells/CaptureCell.cpp @@ -19,7 +19,7 @@ CaptureCell::CaptureCell(HttpInfo* request, const CCSize& size, const std::funct } bool CaptureCell::init(const CCSize& size) { - const std::string method(m_request->formatMethod()); + const std::string method(m_request->stringifyMethod()); std::string path(m_request->getPath()); std::string cutoffPath(path.substr(path.find_last_of('/'))); @@ -54,22 +54,28 @@ bool CaptureCell::init(const CCSize& size) { } for (size_t i = 0; i < method.size(); i++) { - cocos::getChild(label, i)->setColor(m_request->colorForMethod()); + cocos::getChild(label, i)->setColor(this->colorForMethod()); } return true; } -void CaptureCell::activate() { - m_request->setActive(true); +ccColor3B CaptureCell::colorForMethod() { + switch (m_request->getMethod()) { + case CCHttpRequest::kHttpGet: return { 0xA8, 0x96, 0xFF }; + case CCHttpRequest::kHttpPost: return { 0x7E, 0xCF, 0x2B }; + case CCHttpRequest::kHttpPut: return { 0xFF, 0x9A, 0x1F }; + case CCHttpRequest::kHttpDelete: return { 0xFF, 0x56, 0x31 }; + default: return { 0x46, 0xC1, 0xE6 }; + } +} +void CaptureCell::activate() { m_switchCell(this); as(m_mainLayer->getChildByIDRecursive("view"_spr))->updateBGImage("GJ_button_05.png"); } void CaptureCell::deactivate() { - m_request->setActive(false); - as(m_mainLayer->getChildByIDRecursive("view"_spr))->updateBGImage("GJ_button_01.png"); } diff --git a/src/nodes/cells/CaptureCell.hpp b/src/nodes/cells/CaptureCell.hpp index b42d39a..6f9ddad 100644 --- a/src/nodes/cells/CaptureCell.hpp +++ b/src/nodes/cells/CaptureCell.hpp @@ -1,7 +1,6 @@ #pragma once #include "../../include.hpp" -#include "../../objects/HttpInfo.hpp" struct CaptureCell : public GenericListCell { static CaptureCell* create(HttpInfo* request, const CCSize& size, const std::function& switchCell); @@ -14,5 +13,6 @@ struct CaptureCell : public GenericListCell { CaptureCell(HttpInfo* request, const CCSize& size, const std::function& switchCell); bool init(const CCSize& size); + ccColor3B colorForMethod(); void onView(CCObject* obj); }; \ No newline at end of file diff --git a/src/nodes/cells/CodeLineCell.hpp b/src/nodes/cells/CodeLineCell.hpp index 8903b52..4cf4fa6 100644 --- a/src/nodes/cells/CodeLineCell.hpp +++ b/src/nodes/cells/CodeLineCell.hpp @@ -1,7 +1,6 @@ #pragma once #include "../../include.hpp" -#include "../../objects/HttpInfo.hpp" #include "../../objects/JSONColor.hpp" #include "../../objects/ThemeStyle.hpp" diff --git a/src/nodes/lists/CaptureList.cpp b/src/nodes/lists/CaptureList.cpp index bfbbb59..e1b6a97 100644 --- a/src/nodes/lists/CaptureList.cpp +++ b/src/nodes/lists/CaptureList.cpp @@ -1,5 +1,7 @@ #include "CaptureList.hpp" +HttpInfo* CaptureList::active = nullptr; + CaptureList* CaptureList::create(const CCSize& size, const float cellHeight, const std::function& switchInfo) { CaptureList* instance = new CaptureList(); @@ -21,10 +23,11 @@ bool CaptureList::init(const CCSize& size, const float cellHeight, const std::fu CCTouchDispatcher* dispatcher = CCTouchDispatcher::get(); CCArrayExt entries; - bool active = false; - for (HttpInfo* request : HttpInfo::requests) { + for (ProxyHandler* proxy : context::CACHED_PROXIES) { + HttpInfo* request = proxy->getInfo(); CaptureCell* capture = CaptureCell::create(request, { size.width, cellHeight }, [this, request, switchInfo](CaptureCell* cell) { + active = request; switchInfo(request); if (m_list) { @@ -38,9 +41,8 @@ bool CaptureList::init(const CCSize& size, const float cellHeight, const std::fu } }); - if (request->isActive() && !active) { + if (request == CaptureList::active) { capture->activate(); - active = true; } entries.push_back(capture); @@ -51,7 +53,7 @@ bool CaptureList::init(const CCSize& size, const float cellHeight, const std::fu } this->setContentSize(size); - this->addChild(m_list = TouchFixList::create(entries.inner(), cellHeight, size.width, this->getContentHeight())); + this->addChild(m_list = ListView::create(entries.inner(), cellHeight, size.width, this->getContentHeight())); return true; } \ No newline at end of file diff --git a/src/nodes/lists/CaptureList.hpp b/src/nodes/lists/CaptureList.hpp index 8d2ddf9..1e4f2e2 100644 --- a/src/nodes/lists/CaptureList.hpp +++ b/src/nodes/lists/CaptureList.hpp @@ -1,14 +1,14 @@ #pragma once #include "../../include.hpp" -#include "TouchFixList.hpp" #include "../cells/CaptureCell.hpp" -#include "../../objects/HttpInfo.hpp" struct CaptureList : public CCNode { static CaptureList* create(const CCSize& size, const float cellHeight, const std::function& switchInfo); private: - TouchFixList* m_list; + static HttpInfo* active; + + ListView* m_list; bool init(const CCSize& size, const float cellHeight, const std::function& switchInfo); }; \ No newline at end of file diff --git a/src/nodes/lists/JSONCodeBlock.cpp b/src/nodes/lists/JSONCodeBlock.cpp index f74f9cf..6b036ec 100644 --- a/src/nodes/lists/JSONCodeBlock.cpp +++ b/src/nodes/lists/JSONCodeBlock.cpp @@ -46,7 +46,7 @@ void JSONCodeBlock::setCode(const HttpInfo::content& code) { } - TouchFixList* list = TouchFixList::create(cells, cellHeight, size.width, size.height); + ListView* list = ListView::create(cells, cellHeight, size.width, size.height); TracklessScrollbar* scrollbar = TracklessScrollbar::create({ PADDING, this->getContentHeight() - 2 }, list); list->setCellOpacity(0); diff --git a/src/nodes/lists/JSONCodeBlock.hpp b/src/nodes/lists/JSONCodeBlock.hpp index 8fadc03..b6516ce 100644 --- a/src/nodes/lists/JSONCodeBlock.hpp +++ b/src/nodes/lists/JSONCodeBlock.hpp @@ -1,10 +1,8 @@ #pragma once #include "../../include.hpp" -#include "TouchFixList.hpp" #include "../cells/CodeLineCell.hpp" #include "../TracklessScrollbar.hpp" -#include "../../objects/HttpInfo.hpp" #include "../../objects/JSONColor.hpp" #include "../../objects/ThemeStyle.hpp" diff --git a/src/nodes/lists/TouchFixList.cpp b/src/nodes/lists/TouchFixList.cpp deleted file mode 100644 index 5ee27f4..0000000 --- a/src/nodes/lists/TouchFixList.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "TouchFixList.hpp" - -TouchFixList* TouchFixList::create(CCArray* cells, const float cellHeight, const float width, const float height) { - TouchFixList* instance = new TouchFixList(); - - if (instance) { - instance->m_itemSeparation = cellHeight; - instance->m_primaryCellColor = BROWN_3B; - instance->m_secondaryCellColor = BROWN_3B; - instance->m_cellOpacity = 0xFF; - instance->m_cellBorderColor = ccc4(0x00, 0x00, 0x00, 0x4B); - - if (instance->init(cells, BoomListType::Default, width, height)) { - instance->autorelease(); - - return instance; - } - } - - CC_SAFE_DELETE(instance); - - return nullptr; -} - -void TouchFixList::draw() { - CCTouchDispatcher* dispatcher = CCTouchDispatcher::get(); - CCTouchHandler* popupHandler = dispatcher->findHandler(as*>(CCDirector::sharedDirector()->getRunningScene()->getChildByID("intercept_popup"_spr))); - CCTouchHandler* listHandler = dispatcher->findHandler(m_tableView); - - ListView::draw(); - - #ifndef GEODE_ANDROID - if (popupHandler && listHandler) { - dispatcher->setPriority(popupHandler->getPriority(), listHandler->getDelegate()); - } - #endif -} \ No newline at end of file diff --git a/src/nodes/lists/TouchFixList.hpp b/src/nodes/lists/TouchFixList.hpp deleted file mode 100644 index 2419247..0000000 --- a/src/nodes/lists/TouchFixList.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "../../include.hpp" - -struct TouchFixList : public ListView { - static TouchFixList* create(CCArray* cells, const float cellHeight, const float width, const float height); -private: - void draw() override; -}; \ No newline at end of file diff --git a/src/objects/HttpInfo.cpp b/src/objects/HttpInfo.cpp deleted file mode 100644 index 07daf73..0000000 --- a/src/objects/HttpInfo.cpp +++ /dev/null @@ -1,234 +0,0 @@ -#include "HttpInfo.hpp" - -std::vector HttpInfo::requests; - -FormToJson HttpInfo::formToJson; - -RobTopToJson HttpInfo::robtopToJson; - -BinaryToRaw HttpInfo::binaryToRaw; - -HttpInfo* HttpInfo::create(CCHttpRequest* request) { - HttpInfo* requestInfo = new HttpInfo(request); - - requestInfo->retain(); - - if (!Mod::get()->getSettingValue("remember-requests") && HttpInfo::requests.size() > 0) { - delete HttpInfo::requests.at(0); - - HttpInfo::requests.resize(0); - } - - HttpInfo::requests.insert(HttpInfo::requests.begin(), requestInfo); - - return requestInfo; -} - -HttpInfo::HttpInfo(CCHttpRequest* request) : m_active(false), - m_method(request->getRequestType()), - m_query(json::object()), - m_headers(json::object()), - m_responseCode(102), - m_responseContentType(ContentType::UNKNOWN_CONTENT), - m_originalTarget(request->getTarget()), - m_originalProxy(request->getSelector()) { - const char* body = request->getRequestData(); - const std::string url(request->getUrl()); - const size_t protocolEnd = url.find("://"); - const size_t queryStart = url.find('?'); - size_t pathStart; - - this->resetCache(); - - if (protocolEnd == std::string::npos) { - pathStart = url.find('/'); - m_protocol = Protocol::UNKNOWN_PROTOCOL; - m_host = url.substr(0, pathStart == std::string::npos ? queryStart : pathStart); - } else { - const std::string protocol(url.substr(0, protocolEnd)); - - if (protocol == "https") { - m_protocol = Protocol::HTTPS; - } else if (protocol == "http") { - m_protocol = Protocol::HTTP; - } else { - m_protocol = Protocol::UNKNOWN_PROTOCOL; - } - - pathStart = url.find('/', protocolEnd + 3); - m_host = url.substr(protocolEnd + 3, (pathStart == std::string::npos ? queryStart : pathStart) - protocolEnd - 3); - } - - // CCHttpRequest headers technically allow for weird header formats, but I'm assuming they're all key-value pairs separated by a colon since this is the standard - // If you don't follow the standard, I'm not going to care that you get shitty results - for (gd::string header : request->getHeaders()) { - const std::string headerStr(header.c_str()); - const size_t colonPos = headerStr.find(":"); - - if (colonPos == std::string::npos) { - m_headers[headerStr] = json(); - } else { - std::string value(headerStr.substr(colonPos + 1)); - - m_headers[headerStr.substr(0, colonPos)] = json(value.erase(0, value.find_first_not_of(' '))); - } - } - - if (pathStart == std::string::npos) { - m_path = "/"; - } else { - m_path = url.substr(pathStart, queryStart == std::string::npos ? std::string::npos : queryStart - pathStart); - } - - if (queryStart != std::string::npos) { - m_query = HttpInfo::formToJson.convert(m_path, url.substr(queryStart + 1)); - } - - m_body = body ? std::string(request->getRequestData()).substr(0, request->getRequestDataSize()) : ""; - m_bodyContentType = this->determineContentType(m_body, true); - request->setResponseCallback(this, httpresponse_selector(HttpInfo::onResponse)); -} - -bool HttpInfo::isActive() { - return m_active; -} - -void HttpInfo::setActive(const bool active) { - m_active = active; -} - -std::string HttpInfo::getHost() { - return m_host; -} - -std::string HttpInfo::getPath() { - return m_path; -} - -HttpInfo::ContentType HttpInfo::getBodyContentType() { - return m_bodyContentType; -} - -HttpInfo::ContentType HttpInfo::getResponseContentType() { - return m_responseContentType; -} - -std::string HttpInfo::formatProtocol() { - switch (m_protocol) { - case Protocol::HTTP: return "HTTP"; - case Protocol::HTTPS: return "HTTPS"; - default: return "UNKNOWN"; - } -} - -std::string HttpInfo::formatMethod() { - switch (m_method) { - case CCHttpRequest::kHttpGet: return "GET"; - case CCHttpRequest::kHttpPost: return "POST"; - case CCHttpRequest::kHttpPut: return "PUT"; - case CCHttpRequest::kHttpDelete: return "DELETE"; - default: return "UNKNOWN"; - } -} - -std::string HttpInfo::formatQuery() { - return m_query.dump(2); -} - -std::string HttpInfo::formatHeaders() { - return m_headers.dump(2); -} - -HttpInfo::content HttpInfo::formatBody() { - return this->getContent(m_bodyContentType, m_body, m_simplifiedBodyCache); -} - -HttpInfo::content HttpInfo::formatResponse() { - return this->getContent(m_responseContentType, m_response, m_simplifiedResponseCache); -} - -unsigned int HttpInfo::getResponseCode() { - return m_responseCode; -} - -std::string HttpInfo::generateBasicInfo(const bool withStatus) { - const std::string info(fmt::format("Method: {}\nProtocol: {}\nHost: {}\nPath: {}", - this->formatMethod(), - this->formatProtocol(), - m_host, - m_path)); - - if (withStatus) { - return fmt::format("Status Code: {}\n{}", m_responseCode, info); - } else { - return info; - } -} - -ccColor3B HttpInfo::colorForMethod() { - switch (m_method) { - case CCHttpRequest::kHttpGet: return { 0xA8, 0x96, 0xFF }; - case CCHttpRequest::kHttpPost: return { 0x7E, 0xCF, 0x2B }; - case CCHttpRequest::kHttpPut: return { 0xFF, 0x9A, 0x1F }; - case CCHttpRequest::kHttpDelete: return { 0xFF, 0x56, 0x31 }; - default: return { 0x46, 0xC1, 0xE6 }; - } -} - -void HttpInfo::resetCache() { - m_simplifiedBodyCache = { ContentType::UNKNOWN_CONTENT, "" }; - m_simplifiedResponseCache = { ContentType::UNKNOWN_CONTENT, "" }; -} - -HttpInfo::content HttpInfo::getContent(const ContentType originalContentType, const std::string& original, HttpInfo::content& cache) { - if (Mod::get()->getSettingValue("raw-data")) { - return { cache.first, original }; - } else if (cache.second.empty()) { - const content simplified = this->simplifyContent({ originalContentType, original }); - - if (Mod::get()->getSettingValue("cache")) { - cache = simplified; - } - - return simplified; - } else { - return cache; - } -} - -HttpInfo::content HttpInfo::simplifyContent(const HttpInfo::content& content) { - switch (content.first) { - case ContentType::FORM: return { ContentType::JSON, HttpInfo::formToJson.convert(m_path, content.second).dump(2) }; - case ContentType::ROBTOP: return { ContentType::JSON, HttpInfo::robtopToJson.convert(m_path, content.second).dump(2) }; - case ContentType::BINARY: return { ContentType::BINARY, HttpInfo::binaryToRaw.convert(content.second) }; - default: return content; - } -} - -HttpInfo::ContentType HttpInfo::determineContentType(const std::string& content, const bool isBody) { - if (content.empty()) { - return ContentType::UNKNOWN_CONTENT; - } else if (HttpInfo::binaryToRaw.canConvert(content)) { - return ContentType::BINARY; - } else if (JsonConverter::isJson(content)) { - return ContentType::JSON; - } else if (content.starts_with('<')) { - return ContentType::XML; - } else if (!isBody && HttpInfo::robtopToJson.canConvert(m_path, content)) { - return ContentType::ROBTOP; - } else if (HttpInfo::formToJson.canConvert(m_path, content)) { - return ContentType::FORM; - } else { - return ContentType::UNKNOWN_CONTENT; - } -} - -void HttpInfo::onResponse(CCHttpClient* client, CCHttpResponse* response) { - gd::vector* data = response->getResponseData(); - - m_response = std::string(data->begin(), data->end()); - m_responseCode = response->getResponseCode(); - m_responseContentType = this->determineContentType(m_response); - - (m_originalTarget->*m_originalProxy)(client, response); -} \ No newline at end of file diff --git a/src/objects/HttpInfo.hpp b/src/objects/HttpInfo.hpp deleted file mode 100644 index f18ab76..0000000 --- a/src/objects/HttpInfo.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -#include "../include.hpp" -#include "converters/FormToJson.hpp" -#include "converters/RobTopToJson.hpp" -#include "converters/JsonConverter.hpp" -#include "converters/BinaryToRaw.hpp" - -struct HttpInfo : public CCObject { - enum Protocol { - HTTP, - HTTPS, - UNKNOWN_PROTOCOL - }; - - enum ContentType { - FORM, - JSON, - XML, - ROBTOP, - BINARY, - UNKNOWN_CONTENT - }; - - typedef std::pair content; - - static std::vector requests; - - static HttpInfo* create(CCHttpRequest* request); - - HttpInfo(CCHttpRequest* request); - bool isActive(); - void setActive(const bool active); - std::string getHost(); - std::string getPath(); - ContentType getBodyContentType(); - ContentType getResponseContentType(); - std::string formatProtocol(); - std::string formatMethod(); - std::string formatQuery(); - std::string formatHeaders(); - content formatBody(); - content formatResponse(); - unsigned int getResponseCode(); - std::string generateBasicInfo(const bool withStatus = true); - ccColor3B colorForMethod(); - void resetCache(); -private: - static FormToJson formToJson; - static RobTopToJson robtopToJson; - static BinaryToRaw binaryToRaw; - - bool m_active; - Protocol m_protocol; - CCHttpRequest::HttpRequestType m_method; - std::string m_host; - std::string m_path; - std::string m_body; - ContentType m_bodyContentType; - std::string m_response; - unsigned int m_responseCode; - ContentType m_responseContentType; - json m_query; - json m_headers; - CCObject* m_originalTarget; - SEL_HttpResponse m_originalProxy; - content m_simplifiedBodyCache; - content m_simplifiedResponseCache; - - content getContent(const ContentType originalContentType, const std::string& original, content& cache); - content simplifyContent(const content& content); - ContentType determineContentType(const std::string& content, const bool isBody = false); - void onResponse(CCHttpClient* client, CCHttpResponse* response); -}; \ No newline at end of file diff --git a/src/objects/converters/BinaryToRaw.hpp b/src/objects/converters/BinaryToRaw.hpp deleted file mode 100644 index 1452d7a..0000000 --- a/src/objects/converters/BinaryToRaw.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "../../include.hpp" - -// What can I say? I like it raw -struct BinaryToRaw { - bool canConvert(const std::string& original); - std::string convert(const std::string& original); -}; \ No newline at end of file diff --git a/src/objects/converters/FormToJson.hpp b/src/objects/converters/FormToJson.hpp deleted file mode 100644 index a10bc67..0000000 --- a/src/objects/converters/FormToJson.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include "JsonConverter.hpp" - -struct FormToJson : public JsonConverter { - bool canConvert(const std::string& path, const std::string& original) override; - json convert(const std::string& path, const std::string& original) override; -}; \ No newline at end of file diff --git a/src/objects/converters/JsonConverter.hpp b/src/objects/converters/JsonConverter.hpp deleted file mode 100644 index b47b871..0000000 --- a/src/objects/converters/JsonConverter.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "../../include.hpp" - -struct JsonConverter { - static bool isInt(const std::string& str); - static bool isNumber(const std::string& str); - static bool isBool(const std::string& str); - static bool isNull(const std::string& str); - static bool isString(const std::string& str); - static bool isJson(const std::string& str); - static json getPrimitiveType(const std::string& key, const std::string& str); - - virtual bool canConvert(const std::string& path, const std::string& original) = 0; - virtual json convert(const std::string& path, const std::string& original) = 0; -}; \ No newline at end of file diff --git a/src/objects/converters/RobTopToJson.hpp b/src/objects/converters/RobTopToJson.hpp deleted file mode 100644 index 09b5824..0000000 --- a/src/objects/converters/RobTopToJson.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "JsonConverter.hpp" - -struct RobTopToJson : public JsonConverter { - enum ObjectType { - OBJECT, - ARRAY - }; - - enum SeparatorType { - KEY_VALUE, - TUPLE - }; - - // The only reason why const char* is involved here is because std::string basically sees everything as a string ctor so overloading with it was almost impossible - struct ObjParser { - ObjParser(const char* delimiter, const char* entryDelimiter = ""); - ObjParser(const std::vector& tupleKeys, const char* delimiter = "", const char* entryDelimiter = ""); - virtual json parse(const std::string& str) const; - private: - ObjectType m_bodyType; - SeparatorType m_separatorType; - std::string m_delimiter; - std::string m_entryDelimiter; - std::vector m_tupleKeys; - - static std::vector split(const std::string& str, const std::string& delimiter); - - json parseEntry(const std::string& str) const; - }; - - struct Parser : public ObjParser { - Parser(const char* delimiter, const std::vector>& metadataKeys = {}); - Parser(const std::vector& tupleKeys, const char* delimiter = "", const std::vector>& metadataKeys = {}); - Parser(const char* delimiter, const char* entryDelimiter, const std::vector>& metadataKeys = {}); - Parser(const std::vector& tupleKeys, const char* delimiter, const char* entryDelimiter, const std::vector>& metadataKeys = {}); - json parse(const std::string& str) const override; - private: - std::vector> m_metadata; - }; - - static const std::unordered_map parsers; - - bool canConvert(const std::string& path, const std::string& original) override; - json convert(const std::string& path, const std::string& original) override; -}; \ No newline at end of file diff --git a/src/proxy/HttpInfo.cpp b/src/proxy/HttpInfo.cpp new file mode 100644 index 0000000..8753240 --- /dev/null +++ b/src/proxy/HttpInfo.cpp @@ -0,0 +1,199 @@ +#include "../../proxy/HttpInfo.hpp" + +proxy::converters::FormToJson proxy::HttpInfo::formToJson; + +proxy::converters::RobTopToJson proxy::HttpInfo::robtopToJson; + +proxy::converters::BinaryToRaw proxy::HttpInfo::binaryToRaw; + +proxy::HttpInfo::HttpInfo(CCHttpRequest* request) : m_method(request->getRequestType()), + m_query(json::object()), + m_headers(json::object()), + m_statusCode(102), + m_responseContentType(ContentType::UNKNOWN_CONTENT) { + const char* body = request->getRequestData(); + + this->resetCache(); + this->parseUrl(request->getUrl()); + this->determineOrigin(); + + // CCHttpRequest headers technically allow for weird header formats, but I'm assuming they're all key-value pairs separated by a colon since this is the standard + // If you don't follow the standard, I'm not going to care that you get shitty results + for (const gd::string& header : request->getHeaders()) { + const std::string headerStr(header.c_str()); + const size_t colonPos = headerStr.find(":"); + + if (colonPos == std::string::npos) { + m_headers[headerStr] = json(); + } else { + std::string value(headerStr.substr(colonPos + 1)); + + m_headers[headerStr.substr(0, colonPos)] = json(value.erase(0, value.find_first_not_of(' '))); + } + } + + m_body = body ? std::string(request->getRequestData()).substr(0, request->getRequestDataSize()) : ""; + m_bodyContentType = this->determineContentType(m_body, true); +} + +proxy::HttpInfo::HttpInfo(web::WebRequest* request, const std::string& method, const std::string& url) : m_query(json::object()), + m_headers(json::object()), + m_statusCode(102), + m_responseContentType(ContentType::UNKNOWN_CONTENT) { + // this->resetCache(); + // this->parseUrl(url); + + // for (const auto& [key, value] : request->m_impl->headers()) { + // m_headers[key] = json(value); + // } + + // m_body = request->m_impl->body(); + // m_bodyContentType = this->determineContentType(m_body, true); +} + +std::string proxy::HttpInfo::stringifyProtocol() const { + switch (m_protocol) { + case Protocol::HTTP: return "HTTP"; + case Protocol::HTTPS: return "HTTPS"; + default: return "UNKNOWN"; + } +} + +std::string proxy::HttpInfo::stringifyMethod() const { + switch (m_method) { + case CCHttpRequest::kHttpGet: return "GET"; + case CCHttpRequest::kHttpPost: return "POST"; + case CCHttpRequest::kHttpPut: return "PUT"; + case CCHttpRequest::kHttpDelete: return "DELETE"; + default: return "UNKNOWN"; + } +} + +std::string proxy::HttpInfo::stringifyQuery() const { + return m_query.dump(2); +} + +std::string proxy::HttpInfo::stringifyHeaders() const { + return m_headers.dump(2); +} + +proxy::HttpInfo::content proxy::HttpInfo::getBodyContent(const bool raw) { + return this->getContent(raw, m_bodyContentType, m_body, m_simplifiedBodyCache); +} + +proxy::HttpInfo::content proxy::HttpInfo::getResponseContent(const bool raw) { + return this->getContent(raw, m_responseContentType, m_response, m_simplifiedResponseCache); +} + +void proxy::HttpInfo::resetCache() { + m_simplifiedBodyCache = { ContentType::UNKNOWN_CONTENT, "" }; + m_simplifiedResponseCache = { ContentType::UNKNOWN_CONTENT, "" }; +} + +proxy::HttpInfo::content proxy::HttpInfo::getContent(const bool raw, const ContentType originalContentType, const std::string& original, content& cache) { + if (raw) { + return { originalContentType, original }; + } else if (cache.second.empty()) { + const content simplified = this->simplifyContent({ originalContentType, original }); + + if (Mod::get()->getSettingValue("cache")) { + cache = simplified; + } + + return simplified; + } else { + return cache; + } +} + +proxy::HttpInfo::content proxy::HttpInfo::simplifyContent(const content& content) { + switch (content.first) { + case ContentType::FORM: return { + ContentType::JSON, + HttpInfo::formToJson.convert(m_path, content.second).dump(2) + }; + case ContentType::ROBTOP: return { + ContentType::JSON, + HttpInfo::robtopToJson.convert(m_path, content.second).dump(2) + }; + case ContentType::BINARY: return { + ContentType::BINARY, + HttpInfo::binaryToRaw.convert(m_path, content.second) + }; + default: return content; + } +} + +proxy::HttpInfo::ContentType proxy::HttpInfo::determineContentType(const std::string& content, const bool isBody) { + if (content.empty()) { + return ContentType::UNKNOWN_CONTENT; + } else if (HttpInfo::binaryToRaw.canConvert(m_path, content)) { + return ContentType::BINARY; + } else if (converters::isJson(content)) { + return ContentType::JSON; + } else if (content.starts_with('<')) { + return ContentType::XML; + } else if (!isBody && HttpInfo::robtopToJson.canConvert(m_path, content)) { + return ContentType::ROBTOP; + } else if (HttpInfo::formToJson.canConvert(m_path, content)) { + return ContentType::FORM; + } else { + return ContentType::UNKNOWN_CONTENT; + } +} + +bool proxy::HttpInfo::isDomain(const std::string& domain) { + return m_host == domain || m_host.ends_with("." + domain); +} + +void proxy::HttpInfo::determineOrigin() { + if (this->isDomain("boomlings.com") || this->isDomain("geometrydash.com")) { + m_origin = Origin::GD; + } else if (this->isDomain("geometrydashfiles.b-cdn.net") || this->isDomain("geometrydashcontent.b-cdn.net")) { + m_origin = Origin::GD_CDN; + // robtop.games is currently in the process of being sold + } else if (this->isDomain("robtopgames.org") || this->isDomain("robtopgames.net") || this->isDomain("robtopgames.com") || this->isDomain("robtop.games")) { + m_origin = Origin::ROBTOP_GAMES; + } else if (this->isDomain("ngfiles.com")) { + m_origin = Origin::NEWGROUNDS; + } else if (this->isDomain("geode-sdk.org")) { + m_origin = Origin::GEODE; + } else { + m_origin = Origin::OTHER; + } +} + +void proxy::HttpInfo::parseUrl(const std::string& url) { + const size_t protocolEnd = url.find("://"); + const size_t queryStart = url.find('?'); + size_t pathStart; + + if (protocolEnd == std::string::npos) { + pathStart = url.find('/'); + m_protocol = Protocol::UNKNOWN_PROTOCOL; + m_host = url.substr(0, pathStart == std::string::npos ? queryStart : pathStart); + } else { + const std::string protocol(url.substr(0, protocolEnd)); + + if (protocol == "https") { + m_protocol = Protocol::HTTPS; + } else if (protocol == "http") { + m_protocol = Protocol::HTTP; + } else { + m_protocol = Protocol::UNKNOWN_PROTOCOL; + } + + pathStart = url.find('/', protocolEnd + 3); + m_host = url.substr(protocolEnd + 3, (pathStart == std::string::npos ? queryStart : pathStart) - protocolEnd - 3); + } + + if (pathStart == std::string::npos) { + m_path = "/"; + } else { + m_path = url.substr(pathStart, queryStart == std::string::npos ? std::string::npos : queryStart - pathStart); + } + + if (queryStart != std::string::npos) { + m_query = HttpInfo::formToJson.convert(m_path, url.substr(queryStart + 1)); + } +} \ No newline at end of file diff --git a/src/proxy/Proxy.cpp b/src/proxy/Proxy.cpp new file mode 100644 index 0000000..45fa6ac --- /dev/null +++ b/src/proxy/Proxy.cpp @@ -0,0 +1,23 @@ +#include "../../proxy/Proxy.hpp" + +proxy::ProxyEvent::ProxyEvent(HttpInfo* info) : m_info(info) { } + +proxy::HttpInfo* proxy::ProxyEvent::getRequest() const { + return m_info; +} + +proxy::RequestEvent::RequestEvent(HttpInfo* info) : ProxyEvent(info) { } + +proxy::ResponseEvent::ResponseEvent(HttpInfo* info) : ProxyEvent(info) { } + +proxy::HttpInfo::content proxy::ResponseEvent::getResponse(const bool raw) const { + return m_info->getResponseContent(raw); +} + +proxy::RequestFilter::RequestFilter(const SourceFilter source) : ProxyFilter(source) { } + +proxy::RequestFilter::RequestFilter(const SourceFilter source, const std::initializer_list& urls) : ProxyFilter(source, urls) { } + +proxy::ResponseFilter::ResponseFilter(const SourceFilter source) : ProxyFilter(source) { } + +proxy::ResponseFilter::ResponseFilter(const SourceFilter source, const std::initializer_list& urls) : ProxyFilter(source, urls) { } \ No newline at end of file diff --git a/src/proxy/ProxyHandler.cpp b/src/proxy/ProxyHandler.cpp new file mode 100644 index 0000000..cec9c0e --- /dev/null +++ b/src/proxy/ProxyHandler.cpp @@ -0,0 +1,50 @@ +#include "ProxyHandler.hpp" + +ProxyHandler* ProxyHandler::create(CCHttpRequest* request) { + ProxyHandler* instance = new ProxyHandler(request); + + instance->retain(); + RequestEvent(instance->getInfo()).post(); + + return instance; +} + +ProxyHandler* ProxyHandler::create(web::WebRequest* request, const std::string& method, const std::string& url) { + ProxyHandler* instance = new ProxyHandler(request, method, url); + + instance->retain(); + RequestEvent(instance->getInfo()).post(); + + return instance; +} + +ProxyHandler::ProxyHandler(CCHttpRequest* request) : m_info(new HttpInfo(request)), + m_originalTarget(request->getTarget()), + m_originalProxy(request->getSelector()) { + request->setResponseCallback(this, httpresponse_selector(ProxyHandler::onResponse)); +} + +ProxyHandler::~ProxyHandler() { + delete m_info; +} + +ProxyHandler::ProxyHandler(web::WebRequest* request, const std::string& method, const std::string& url) : m_info(new HttpInfo(request, method, url)), + m_originalTarget(nullptr), + m_originalProxy(nullptr) { + // request->setResponseCallback(this, httpresponse_selector(ProxyHandler::onResponse)); +} + +HttpInfo* ProxyHandler::getInfo() { + return m_info; +} + +void ProxyHandler::onResponse(CCHttpClient* client, CCHttpResponse* response) { + gd::vector* data = response->getResponseData(); + + m_info->m_response = std::string(data->begin(), data->end()); + m_info->m_statusCode = response->getResponseCode(); + m_info->m_responseContentType = m_info->determineContentType(m_info->m_response); + + (m_originalTarget->*m_originalProxy)(client, response); + ResponseEvent(m_info).post(); +} \ No newline at end of file diff --git a/src/proxy/ProxyHandler.hpp b/src/proxy/ProxyHandler.hpp new file mode 100644 index 0000000..b2c1c27 --- /dev/null +++ b/src/proxy/ProxyHandler.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include "../include.hpp" + +namespace proxy { + struct ProxyHandler : public CCObject { + static ProxyHandler* create(CCHttpRequest* request); + static ProxyHandler* create(web::WebRequest* request, const std::string& method, const std::string& url); + + HttpInfo* getInfo(); + ~ProxyHandler(); + private: + HttpInfo* m_info; + CCObject* m_originalTarget; + SEL_HttpResponse m_originalProxy; + + ProxyHandler(CCHttpRequest* request); + ProxyHandler(web::WebRequest* request, const std::string& method, const std::string& url); + void onResponse(CCHttpClient* client, CCHttpResponse* response); + }; +} \ No newline at end of file diff --git a/src/objects/converters/BinaryToRaw.cpp b/src/proxy/converters/BinaryToRaw.cpp similarity index 66% rename from src/objects/converters/BinaryToRaw.cpp rename to src/proxy/converters/BinaryToRaw.cpp index b27230a..15c0897 100644 --- a/src/objects/converters/BinaryToRaw.cpp +++ b/src/proxy/converters/BinaryToRaw.cpp @@ -1,10 +1,10 @@ -#include "BinaryToRaw.hpp" +#include "../../../proxy/converters/BinaryToRaw.hpp" -bool BinaryToRaw::canConvert(const std::string& original) { +bool proxy::converters::BinaryToRaw::canConvert(const std::string& path, const std::string& original) { return original.find_first_of('\0') != std::string::npos; } -std::string BinaryToRaw::convert(const std::string& original) { +std::string proxy::converters::BinaryToRaw::convert(const std::string& path, const std::string& original) { std::stringstream result; result << std::hex << std::setfill('0'); diff --git a/src/objects/converters/JsonConverter.cpp b/src/proxy/converters/Converter.cpp similarity index 68% rename from src/objects/converters/JsonConverter.cpp rename to src/proxy/converters/Converter.cpp index 08cf90f..80c00e1 100644 --- a/src/objects/converters/JsonConverter.cpp +++ b/src/proxy/converters/Converter.cpp @@ -1,6 +1,6 @@ -#include "JsonConverter.hpp" +#include "../../../proxy/converters/Converter.hpp" -bool JsonConverter::isInt(const std::string& str) { +bool proxy::converters::isInt(const std::string& str) { if (str.empty()) { return false; } @@ -10,28 +10,28 @@ bool JsonConverter::isInt(const std::string& str) { return str.substr(negative, str.size() - negative).find_first_not_of("0123456789") == std::string::npos && (!negative || str.size() > 1); } -bool JsonConverter::isNumber(const std::string& str) { +bool proxy::converters::isNumber(const std::string& str) { if (str.empty()) { return false; } const size_t decimalPos = str.find('.'); - return JsonConverter::isInt(str.substr(0, decimalPos)) && ( + return isInt(str.substr(0, decimalPos)) && ( decimalPos == std::string::npos || str.substr(decimalPos + 1).find_first_not_of("0123456789") == std::string::npos ); } -bool JsonConverter::isBool(const std::string& str) { +bool proxy::converters::isBool(const std::string& str) { return str == "true" || str == "false"; } -bool JsonConverter::isNull(const std::string& str) { +bool proxy::converters::isNull(const std::string& str) { return str == "null"; } -bool JsonConverter::isString(const std::string& str) { +bool proxy::converters::isString(const std::string& str) { if (str.empty()) { return false; } @@ -57,29 +57,29 @@ bool JsonConverter::isString(const std::string& str) { } } -bool JsonConverter::isJson(const std::string& str) { +bool proxy::converters::isJson(const std::string& str) { return str.starts_with('{') || str.starts_with('[') || - JsonConverter::isNumber(str) || - JsonConverter::isString(str) || - JsonConverter::isBool(str) || - JsonConverter::isNull(str); + isNumber(str) || + isString(str) || + isBool(str) || + isNull(str); } -json JsonConverter::getPrimitiveType(const std::string& key, const std::string& str) { +nlohmann::json proxy::converters::getPrimitiveJsonType(const std::string& key, const std::string& str) { static const std::vector sensitiveKeys({ "password", "pass", "passwd", "pwd", "token", "tkn", "sID", "gjp", "gjp2" }); if (Mod::get()->getSettingValue("censor-data") && std::find(sensitiveKeys.begin(), sensitiveKeys.end(), key) != sensitiveKeys.end()) { return json("********"); - } else if (JsonConverter::isNull(str)) { + } else if (isNull(str)) { return json(); - } else if (JsonConverter::isBool(str)) { + } else if (isBool(str)) { return json(str == "true"); - } else if (JsonConverter::isInt(str)) { + } else if (isInt(str)) { return json(std::stoll(str)); - } else if (JsonConverter::isNumber(str)) { + } else if (isNumber(str)) { return json(std::stold(str)); - } else if (JsonConverter::isString(str)) { + } else if (isString(str)) { return json(str.substr(1, str.size() - 2)); } else { return json(str); diff --git a/src/objects/converters/FormToJson.cpp b/src/proxy/converters/FormToJson.cpp similarity index 68% rename from src/objects/converters/FormToJson.cpp rename to src/proxy/converters/FormToJson.cpp index e9180ad..d91311e 100644 --- a/src/objects/converters/FormToJson.cpp +++ b/src/proxy/converters/FormToJson.cpp @@ -1,6 +1,6 @@ -#include "FormToJson.hpp" +#include "../../../proxy/converters/FormToJson.hpp" -bool FormToJson::canConvert(const std::string& path, const std::string& original) { +bool proxy::converters::FormToJson::canConvert(const std::string& path, const std::string& original) { std::stringstream stream(original); std::string section; @@ -14,7 +14,7 @@ bool FormToJson::canConvert(const std::string& path, const std::string& original return true; } -json FormToJson::convert(const std::string& path, const std::string& original) { +nlohmann::json proxy::converters::FormToJson::convert(const std::string& path, const std::string& original) { json object(json::object()); std::stringstream stream(original); std::string section; @@ -27,7 +27,7 @@ json FormToJson::convert(const std::string& path, const std::string& original) { } else { const std::string key(section.substr(0, equalPos)); - object[key] = JsonConverter::getPrimitiveType(key, section.substr(equalPos + 1)); + object[key] = getPrimitiveJsonType(key, section.substr(equalPos + 1)); } } diff --git a/src/objects/converters/RobTopToJson.cpp b/src/proxy/converters/RobTopToJson.cpp similarity index 77% rename from src/objects/converters/RobTopToJson.cpp rename to src/proxy/converters/RobTopToJson.cpp index a961e84..bb0bb5a 100644 --- a/src/objects/converters/RobTopToJson.cpp +++ b/src/proxy/converters/RobTopToJson.cpp @@ -1,6 +1,6 @@ -#include "RobTopToJson.hpp" +#include "../../../proxy/converters/RobTopToJson.hpp" -const std::unordered_map RobTopToJson::parsers({ +const std::unordered_map proxy::converters::RobTopToJson::parsers({ // Accounts { "/database/accounts/syncGJAccountNew.php", RobTopToJson::Parser(std::vector({ "encodedResponse" })) }, { "/database/accounts/loginGJAccount.php", RobTopToJson::Parser({ "accountID", "userID" }, ",") }, @@ -68,14 +68,14 @@ const std::unordered_map RobTopToJson::parser { "/database/getSaveData.php", RobTopToJson::Parser(std::vector({ "encodedResponse" })) } }); -RobTopToJson::ObjParser::ObjParser(const char* delimiter, const char* entryDelimiter) : +proxy::converters::RobTopToJson::ObjParser::ObjParser(const char* delimiter, const char* entryDelimiter) : m_delimiter(delimiter), m_entryDelimiter(entryDelimiter), m_separatorType(KEY_VALUE) { m_bodyType = m_entryDelimiter.empty() ? ARRAY : OBJECT; } -RobTopToJson::ObjParser::ObjParser(const std::vector& tupleKeys, const char* delimiter, const char* entryDelimiter) : +proxy::converters::RobTopToJson::ObjParser::ObjParser(const std::vector& tupleKeys, const char* delimiter, const char* entryDelimiter) : m_tupleKeys(tupleKeys), m_delimiter(delimiter), m_entryDelimiter(entryDelimiter), @@ -83,23 +83,23 @@ m_separatorType(TUPLE) { m_bodyType = m_entryDelimiter.empty() ? ARRAY : OBJECT; } -RobTopToJson::Parser::Parser(const char* delimiter, const std::vector>& metadataKeys) : +proxy::converters::RobTopToJson::Parser::Parser(const char* delimiter, const std::vector>& metadataKeys) : ObjParser(delimiter), m_metadata(metadataKeys) {} -RobTopToJson::Parser::Parser(const std::vector& tupleKeys, const char* delimiter, const std::vector>& metadataKeys) : +proxy::converters::RobTopToJson::Parser::Parser(const std::vector& tupleKeys, const char* delimiter, const std::vector>& metadataKeys) : ObjParser(tupleKeys, delimiter), m_metadata(metadataKeys) {} -RobTopToJson::Parser::Parser(const char* delimiter, const char* entryDelimiter, const std::vector>& metadataKeys) : +proxy::converters::RobTopToJson::Parser::Parser(const char* delimiter, const char* entryDelimiter, const std::vector>& metadataKeys) : ObjParser(delimiter, entryDelimiter), m_metadata(metadataKeys) {} -RobTopToJson::Parser::Parser(const std::vector& tupleKeys, const char* delimiter, const char* entryDelimiter, const std::vector>& metadataKeys) : +proxy::converters::RobTopToJson::Parser::Parser(const std::vector& tupleKeys, const char* delimiter, const char* entryDelimiter, const std::vector>& metadataKeys) : ObjParser(tupleKeys, delimiter, entryDelimiter), m_metadata(metadataKeys) {} -std::vector RobTopToJson::ObjParser::split(const std::string& str, const std::string& delimiter) { +std::vector proxy::converters::RobTopToJson::ObjParser::split(const std::string& str, const std::string& delimiter) { std::vector parts; if (delimiter.empty()) { @@ -119,7 +119,7 @@ std::vector RobTopToJson::ObjParser::split(const std::string& str, return parts; } -json RobTopToJson::ObjParser::parse(const std::string& str) const { +nlohmann::json proxy::converters::RobTopToJson::ObjParser::parse(const std::string& str) const { if (m_entryDelimiter.empty()) { return parseEntry(str); } else { @@ -136,7 +136,7 @@ json RobTopToJson::ObjParser::parse(const std::string& str) const { } } -json RobTopToJson::ObjParser::parseEntry(const std::string& str) const { +nlohmann::json proxy::converters::RobTopToJson::ObjParser::parseEntry(const std::string& str) const { const std::vector parts(this->split(str, m_delimiter)); json object(json::object()); std::string lastKey; @@ -145,18 +145,18 @@ json RobTopToJson::ObjParser::parseEntry(const std::string& str) const { if (m_separatorType == TUPLE) { const std::string key(i < m_tupleKeys.size() ? m_tupleKeys.at(i) : fmt::format("unknown{}", i)); - object[key] = JsonConverter::getPrimitiveType(key, parts.at(i)); + object[key] = getPrimitiveJsonType(key, parts.at(i)); } else if (i % 2 == 0) { object[lastKey = parts.at(i)] = json(); } else { - object[lastKey] = JsonConverter::getPrimitiveType(lastKey, parts.at(i)); + object[lastKey] = getPrimitiveJsonType(lastKey, parts.at(i)); } } return object; } -json RobTopToJson::Parser::parse(const std::string& str) const { +nlohmann::json proxy::converters::RobTopToJson::Parser::parse(const std::string& str) const { if (m_metadata.empty()) { return ObjParser::parse(str); } else { @@ -178,12 +178,12 @@ json RobTopToJson::Parser::parse(const std::string& str) const { } } -bool RobTopToJson::canConvert(const std::string& path, const std::string& original) { +bool proxy::converters::RobTopToJson::canConvert(const std::string& path, const std::string& original) { return RobTopToJson::parsers.contains(path); } -json RobTopToJson::convert(const std::string& path, const std::string& original) { - if (JsonConverter::isNumber(original)) { +nlohmann::json proxy::converters::RobTopToJson::convert(const std::string& path, const std::string& original) { + if (isNumber(original)) { return json(std::stold(original)); } else { return RobTopToJson::parsers.at(path).parse(original); diff --git a/src/scenes/InterceptPopup.cpp b/src/scenes/InterceptPopup.cpp index d58f96f..935832f 100644 --- a/src/scenes/InterceptPopup.cpp +++ b/src/scenes/InterceptPopup.cpp @@ -23,7 +23,7 @@ InterceptPopup* InterceptPopup::get() { } void InterceptPopup::scene() { - if (!HttpInfo::requests.empty() && !InterceptPopup::get()) { + if (!context::CACHED_PROXIES.empty() && !InterceptPopup::get()) { InterceptPopup* instance = new InterceptPopup(); if (instance && instance->initAnchored(InterceptPopup::uiWidth, InterceptPopup::uiHeight)) { @@ -57,15 +57,23 @@ bool InterceptPopup::setup() { } void InterceptPopup::reload() { + CCScene* scene = CCDirector::sharedDirector()->getRunningScene(); + const size_t sceneNodeCount = scene->getChildrenCount(); + OPT(m_infoArea)->removeFromParentAndCleanup(true); - OPT(m_settings)->removeFromParentAndCleanup(true); + OPT(m_controls)->removeFromParentAndCleanup(true); OPT(m_codeBlock)->removeFromParentAndCleanup(true); OPT(m_list)->removeFromParentAndCleanup(true); m_infoArea = this->setupInfo(); - m_settings = this->setupSettings(); + m_controls = this->setupControls(); m_codeBlock = this->setupCodeBlock(); m_list = this->setupList(); + + if (m_settings) { + m_settings->removeFromParentAndCleanup(true); + this->onSettings(nullptr); + } } void InterceptPopup::copyCode() { @@ -98,22 +106,18 @@ Border* InterceptPopup::setupList() { return captures; } -Border* InterceptPopup::setupSettings() { - CCSize padding(ccp(PADDING, PADDING)); +ControlMenu* InterceptPopup::setupControls() { const float xPosition = m_infoArea->getPositionX() + m_infoArea->getContentWidth() + PADDING; - const float width = InterceptPopup::uiWidth - xPosition - InterceptPopup::uiPadding; - CCScale9Sprite* settingsBg = CCScale9Sprite::create("square02b_001.png"); - Border* settings = Border::create(settingsBg, LIGHTER_BROWN_4B, { width, InterceptPopup::infoRowHeight }, padding); - CCLabelBMFont* settingsLabel = CCLabelBMFont::create("Coming Soon...", "bigFont.fnt"); - - settingsLabel->setScale(0.5f); - settingsLabel->setPosition(settingsBg->getContentSize() / 2); - settings->setPosition({ xPosition, this->getComponentYPosition(0, InterceptPopup::infoRowHeight) }); - settingsBg->setColor(LIGHT_BROWN_3B); - settingsBg->addChild(settingsLabel); - m_mainLayer->addChild(settings); - - return settings; + + ControlMenu* controls = ControlMenu::create({ + InterceptPopup::uiWidth - xPosition - InterceptPopup::uiPadding, + InterceptPopup::infoRowHeight + }, this->m_settings); + + controls->setPosition({ xPosition, this->getComponentYPosition(0, InterceptPopup::infoRowHeight) }); + m_mainLayer->addChild(controls); + + return controls; } CodeBlock* InterceptPopup::setupCodeBlock() { @@ -144,5 +148,10 @@ void InterceptPopup::onClose(CCObject* obj) { } void InterceptPopup::onSettings(CCObject* obj) { + CCScene* currentScene = CCDirector::sharedDirector()->getRunningScene(); + openSettingsPopup(Mod::get()); + + // If this breaks, someone should stop messing with addChild, that's a whole lot of not my problem + m_settings = as(currentScene->getChildren()->objectAtIndex(currentScene->getChildrenCount() - 1)); } \ No newline at end of file diff --git a/src/scenes/InterceptPopup.hpp b/src/scenes/InterceptPopup.hpp index 259eb0f..4863611 100644 --- a/src/scenes/InterceptPopup.hpp +++ b/src/scenes/InterceptPopup.hpp @@ -5,7 +5,7 @@ #include "../nodes/InfoArea.hpp" #include "../nodes/ButtonBar.hpp" #include "../nodes/CodeBlock.hpp" -#include "../objects/HttpInfo.hpp" +#include "../nodes/ControlMenu.hpp" #include "../objects/ThemeStyle.hpp" #include "../nodes/lists/CaptureList.hpp" #include "../nodes/lists/JSONCodeBlock.hpp" @@ -32,12 +32,13 @@ struct InterceptPopup : public Popup<> { InfoArea* m_infoArea; CodeBlock* m_codeBlock; - Border* m_settings; + ControlMenu* m_controls; Border* m_list; + FLAlertLayer* m_settings; InfoArea* setupInfo(); Border* setupList(); - Border* setupSettings(); + ControlMenu* setupControls(); CodeBlock* setupCodeBlock(); float getPageHeight(); float getComponentYPosition(float offset, float itemHeight);