- Add Support for Downloading Data/Files, do Api Requests
- Refactor Tasks System
  - Now uses std::thread
- The Net  Functions Will Break IDBN server for now
This commit is contained in:
tobid7 2024-05-22 22:16:40 +02:00
parent a0923acec7
commit 6d7781b209
9 changed files with 415 additions and 39 deletions

View File

@ -30,6 +30,8 @@
- Fix Crash in FilsSystem - Fix Crash in FilsSystem
- Implement basic Theme System - Implement basic Theme System
- Remove 0.9.4 Security - Remove 0.9.4 Security
- Tasks now based on std functional/thread
- Add Network Support (Download Files, APi Requests)
## 0.9.4 ## 0.9.4
- Implement new Security System To prevent from crashes - Implement new Security System To prevent from crashes
- Implement Functiontrace for better Timing Tests - Implement Functiontrace for better Timing Tests

View File

@ -25,6 +25,7 @@
#include <renderd7/Hid.hpp> #include <renderd7/Hid.hpp>
#include <renderd7/Image.hpp> #include <renderd7/Image.hpp>
#include <renderd7/Message.hpp> #include <renderd7/Message.hpp>
#include <renderd7/Net.hpp>
#include <renderd7/Overlays.hpp> #include <renderd7/Overlays.hpp>
#include <renderd7/StealConsole.hpp> #include <renderd7/StealConsole.hpp>
#include <renderd7/Timer.hpp> #include <renderd7/Timer.hpp>

60
include/renderd7/Net.hpp Normal file
View File

@ -0,0 +1,60 @@
/**
* This file is part of RenderD7
* Copyright (C) 2021-2024 NPI-D7, tobid7
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <renderd7/external/json.hpp>
#include <string>
namespace RenderD7 {
namespace Net {
// Define List of Errors
enum Error_ {
Error_None, // Function Executed Successfully
Error_Memory, // Memory Allocation Error
Error_Write, // Unable to Write File
Error_StatusCode, // Error with Status Code
Error_Git, // Git Error
Error_CtrStatus, // 3ds Result Code
Error_Curl, // Curl Error
Error_Busy, // Another Download Taskl is already running
Error_Invalid, // Invalid Json struct
Error_NoWifi, // Console not connected to wifi
};
// Set an typedefine for Error code
using Error = unsigned long long;
// Extract Error_ from Error code
inline Error_ ErrorCode(Error err) {
return static_cast<Error_>(static_cast<unsigned int>(err & 0xffffffff));
}
// Extract Http Status code, Curl Error Code or Ctr Result Code
inline int StatusCode(Error err) {
Error_ c = ErrorCode(err);
if (c != Error_StatusCode && c != Error_CtrStatus && c != Error_Curl)
return 0;
return static_cast<unsigned int>(err >> 32);
}
Error Download(const std::string& url, std::string& data);
Error Download2File(const std::string& url, const std::string& path);
Error GitDownloadRelease(const std::string& url, const std::string& asset_name,
const std::string& path, bool prerelease = false);
Error JsonApiRequest(const std::string& api_url, nlohmann::json& res);
unsigned long long GetProgressCurrent();
unsigned long long GetProgressTotal();
} // namespace Net
} // namespace RenderD7

View File

@ -17,14 +17,15 @@
*/ */
#pragma once #pragma once
#include <3ds.h> #include <functional>
namespace RenderD7 { namespace RenderD7 {
namespace Tasks { namespace Tasks {
/// @brief Push A Task /// @brief Push A Task
/// @param entrypoint Function of Your Task /// @param fun Function of Your Task
void create(ThreadFunc entrypoint); /// @return index
int Create(std::function<void()> fun);
/// @brief Destroy all Tasks /// @brief Destroy all Tasks
void destroy(void); void DestroyAll();
} // namespace Tasks } // namespace Tasks
} // namespace RenderD7 } // namespace RenderD7

View File

@ -17,6 +17,7 @@
*/ */
#pragma once #pragma once
#include <renderd7/Net.hpp>
#include <renderd7/external/json.hpp> #include <renderd7/external/json.hpp>
#include <renderd7/global_db.hpp> #include <renderd7/global_db.hpp>
#include <renderd7/renderd7.hpp> #include <renderd7/renderd7.hpp>
@ -72,4 +73,8 @@ extern bool rd7i_fade_exit;
extern bool rd7i_fade_scene_wait; extern bool rd7i_fade_scene_wait;
extern bool rd7i_idb_running; extern bool rd7i_idb_running;
extern bool rd7i_graphics_on; extern bool rd7i_graphics_on;
extern bool rd7i_amdt; extern bool rd7i_amdt;
extern void* rd7i_soc_buf;
RenderD7::Net::Error rd7i_soc_init();
void rd7i_soc_deinit();

294
source/Net.cpp Normal file
View File

@ -0,0 +1,294 @@
/**
* This file is part of RenderD7
* Copyright (C) 2021-2024 NPI-D7, tobid7
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that 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 <http://www.gnu.org/licenses/>.
*/
// TODO: Make Download2File faster on large files
#include <3ds.h>
#include <curl/curl.h>
#include <malloc.h>
#include <unistd.h>
#include <filesystem>
#include <fstream>
#include <regex>
#include <renderd7/Net.hpp>
#include <renderd7/internal_db.hpp>
static RenderD7::Net::Error rd7i_check_wifi() {
// if (rd7i_is_citra) return 0;
int s = osGetWifiStrength();
return (s == 0);
}
static size_t rd7i_handle_data(char* ptr, size_t size, size_t nmemb,
void* userdata) {
size_t ms = size * nmemb;
((std::string*)userdata)->append(ptr, ms);
return ms;
}
static size_t rd7i_handle_file(char* ptr, size_t size, size_t nmemb,
void* out) {
size_t ms = size * nmemb;
((std::ofstream*)out)->write(reinterpret_cast<const char*>(ptr), ms);
return ms;
}
struct rd7i_net_dl {
unsigned long long current = 0;
unsigned long long total = 1;
void Reset() {
current = 0;
total = 1;
}
};
static rd7i_net_dl rd7i_net_dl_spec;
static int rd7i_handle_curl_progress(CURL* hnd, curl_off_t dltotal,
curl_off_t dlnow, curl_off_t ultotal,
curl_off_t ulnow) {
rd7i_net_dl_spec.total = dltotal;
rd7i_net_dl_spec.current = dlnow;
return 0;
}
static void rd7i_setup_curl_context(CURL* hnd, const std::string& url,
void* userptr, bool mem) {
std::string user_agent =
rd7i_app_name + "/RenderD7 (Version: " + std::string(RENDERD7VSTRING) +
")";
if (!mem) {
curl_easy_setopt(hnd, CURLOPT_FAILONERROR, 1L);
curl_easy_setopt(hnd, CURLOPT_ACCEPT_ENCODING, "gzip");
curl_easy_setopt(hnd, CURLOPT_XFERINFOFUNCTION, rd7i_handle_curl_progress);
}
curl_easy_setopt(hnd, CURLOPT_URL, url.c_str());
curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(hnd, CURLOPT_USERAGENT, user_agent.c_str());
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, userptr);
curl_easy_setopt(hnd, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS);
curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION,
mem ? rd7i_handle_data : rd7i_handle_file);
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(hnd, CURLOPT_STDERR, stdout);
}
static bool rd7i_curl_is_busy = false;
static bool rd7i_apir_is_busy = false;
namespace RenderD7 {
namespace Net {
Error Download(const std::string& url, std::string& data) {
if (rd7i_curl_is_busy) return Error_Busy;
Error ret = rd7i_check_wifi();
if (ret != 0) {
if (ret == 1) {
return Error_NoWifi;
} else {
return ret;
}
}
rd7i_curl_is_busy = true;
rd7i_net_dl_spec.Reset();
ret = rd7i_soc_init();
if (ret) {
rd7i_curl_is_busy = false;
return ret;
}
auto hnd = curl_easy_init();
rd7i_setup_curl_context(hnd, url, &data, true);
CURLcode curl_res = curl_easy_perform(hnd);
curl_easy_cleanup(hnd);
if (curl_res != CURLE_OK) {
data.clear();
rd7i_soc_deinit();
rd7i_curl_is_busy = false;
return ((static_cast<Error>(curl_res) << 32) |
static_cast<Error>(Error_Curl));
}
rd7i_soc_deinit();
rd7i_curl_is_busy = false;
return 0;
}
Error Download2File(const std::string& url, const std::string& path) {
if (rd7i_curl_is_busy) return Error_Busy;
Error ret = rd7i_check_wifi();
if (ret != 0) {
if (ret == 1) {
return Error_NoWifi;
} else {
return ret;
}
}
rd7i_curl_is_busy = true;
rd7i_net_dl_spec.Reset();
ret = rd7i_soc_init();
if (ret) {
rd7i_curl_is_busy = false;
return ret;
}
// std::filesystem::create_directories(
// std::filesystem::path(path).remove_filename());
std::ofstream file(path, std::ios::binary);
if (!file.is_open()) {
rd7i_curl_is_busy = false;
return Error_Write;
}
auto hnd = curl_easy_init();
rd7i_setup_curl_context(hnd, url, &file, false);
CURLcode curl_res = curl_easy_perform(hnd);
curl_easy_cleanup(hnd);
file.close();
if (curl_res != CURLE_OK) {
rd7i_soc_deinit();
if (RenderD7::FS::FileExist(path)) {
std::filesystem::remove(path);
}
rd7i_curl_is_busy = false;
return ((static_cast<Error>(curl_res) << 32) |
static_cast<Error>(Error_Curl));
}
rd7i_soc_deinit();
rd7i_curl_is_busy = false;
return 0;
}
Error GitDownloadRelease(const std::string& url, const std::string& asset_name,
const std::string& path, bool prerelease) {
if (rd7i_apir_is_busy) return Error_Busy;
Error ret = rd7i_check_wifi();
if (ret != 0) {
if (ret == 1) {
return Error_NoWifi;
} else {
return ret;
}
}
rd7i_apir_is_busy = true;
std::regex parse("github\\.com\\/(.+)\\/(.+)");
std::smatch res;
std::regex_search(url, res, parse);
std::string user = res[1].str();
std::string repo = res[2].str();
std::stringstream req;
req << "https://api.github.com/repos/" << user << "/" << repo
<< (prerelease ? "/releases" : "/releases/latest");
std::string buf;
ret = Download(req.str(), buf);
if (ret) {
rd7i_apir_is_busy = false;
return ret;
}
int pret = 0;
std::string freq;
if (nlohmann::json::accept(buf)) {
nlohmann::json api = nlohmann::json::parse(buf);
if (!api.size()) pret = -1;
if (pret != -1) {
if (prerelease) api = api[0];
if (api["assets"].is_array()) {
for (const auto& asset : api["assets"]) {
if (asset.is_object() && asset["name"].is_string() &&
asset["browser_download_url"].is_string()) {
if (std::regex_match(std::string(asset["name"]),
std::regex(asset_name))) {
freq = asset["browser_download_url"];
break;
}
}
}
}
}
} else {
pret = -1;
}
if (pret != 0 || freq.empty()) {
rd7i_apir_is_busy = false;
return Error_Git;
}
ret = Download2File(freq, path);
rd7i_apir_is_busy = false;
return ret;
}
Error JsonApiRequest(const std::string& api_url, nlohmann::json& res) {
if (rd7i_apir_is_busy) return Error_Busy;
Error ret = rd7i_check_wifi();
if (ret != 0) {
if (ret == 1) {
return Error_NoWifi;
} else {
return ret;
}
}
rd7i_apir_is_busy = true;
std::string buf;
ret = Download(api_url, buf);
if (ret) {
rd7i_apir_is_busy = false;
return ret;
}
if (nlohmann::json::accept(buf)) {
res = nlohmann::json::parse(buf);
if (!res.size()) {
rd7i_apir_is_busy = false;
return Error_Invalid;
}
} else {
rd7i_apir_is_busy = false;
return Error_Invalid;
}
rd7i_apir_is_busy = false;
return 0;
}
unsigned long long GetProgressCurrent() { return rd7i_net_dl_spec.current; }
unsigned long long GetProgressTotal() {
// As curl sets total to 0 we need
// to return a 1 as devide by zeroi will crash
if (rd7i_net_dl_spec.total <= 0) return 1;
return rd7i_net_dl_spec.total;
}
} // namespace Net
} // namespace RenderD7

View File

@ -16,26 +16,25 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <3ds.h> #include <memory>
#include <stdio.h>
#include <string.h>
#include <renderd7/Tasks.hpp> #include <renderd7/Tasks.hpp>
#include <thread>
#include <vector> #include <vector>
static std::vector<Thread> threads; static std::vector<std::shared_ptr<std::thread>> threads;
void RenderD7::Tasks::create(ThreadFunc entrypoint) { int RenderD7::Tasks::Create(std::function<void()> fun) {
s32 prio = 0; auto thrd = std::make_shared<std::thread>(fun);
svcGetThreadPriority(&prio, CUR_THREAD_HANDLE); threads.push_back(thrd);
Thread thread = threadCreate((ThreadFunc)entrypoint, NULL, 64 * 1024, return threads.size();
prio - 1, -2, false);
threads.push_back(thread);
} }
void RenderD7::Tasks::destroy(void) { void RenderD7::Tasks::DestroyAll() {
for (u32 i = 0; i < threads.size(); i++) { for (auto& it : threads) {
threadJoin(threads.at(i), U64_MAX); if (it && it->joinable()) {
threadFree(threads.at(i)); it->join();
}
} }
// Delete Pointers
threads.clear();
} }

View File

@ -26,6 +26,7 @@
#include <renderd7/FileSystem.hpp> #include <renderd7/FileSystem.hpp>
#include <renderd7/external/json.hpp> #include <renderd7/external/json.hpp>
#include <renderd7/internal_db.hpp>
#include <renderd7/renderd7.hpp> #include <renderd7/renderd7.hpp>
/// Base /// /// Base ///
@ -71,6 +72,7 @@ bool rd7i_idb_running = false;
bool rd7i_graphics_on = false; bool rd7i_graphics_on = false;
float rd7_draw2_tsm = 1.2f; float rd7_draw2_tsm = 1.2f;
bool rd7i_amdt = false; bool rd7i_amdt = false;
void *rd7i_soc_buf = nullptr;
/// Global /// /// Global ///
// Outdated HidApi (HidV2Patched) // Outdated HidApi (HidV2Patched)
@ -94,6 +96,31 @@ C3D_RenderTarget *rd7_top;
C3D_RenderTarget *rd7_top_right; C3D_RenderTarget *rd7_top_right;
C3D_RenderTarget *rd7_bottom; C3D_RenderTarget *rd7_bottom;
RenderD7::Net::Error rd7i_soc_init() {
if (rd7i_soc_buf != nullptr) {
return 0;
}
rd7i_soc_buf = memalign(0x1000, 0x100000);
if (!rd7i_soc_buf) {
return RenderD7::Net::Error_Memory;
}
Result ret = socInit((u32 *)rd7i_soc_buf, 0x100000);
if (R_FAILED(ret)) {
free(rd7i_soc_buf);
return ((static_cast<RenderD7::Net::Error>(ret) << 32) |
static_cast<RenderD7::Net::Error>(RenderD7::Net::Error_CtrStatus));
}
return 0;
}
void rd7i_soc_deinit() {
if (rd7i_soc_buf) {
socExit();
free(rd7i_soc_buf);
}
rd7i_soc_buf = nullptr;
}
class Logger { class Logger {
public: public:
Logger() = default; Logger() = default;
@ -157,10 +184,10 @@ class tcp_server {
#define stupid(x) &x, sizeof(x) #define stupid(x) &x, sizeof(x)
#define rd7i_reacttion(x) \ #define rd7i_reacttion(x) \
({ \ { \
int code = x; \ int code = x; \
server.snd(stupid(code)); \ server.snd(stupid(code)); \
}) }
struct pak32 { struct pak32 {
pak32() {} pak32() {}
@ -206,30 +233,16 @@ struct pak32 {
unsigned int tbs; unsigned int tbs;
}; };
#define SOC_ALIGN 0x1000
#define SOC_BUFFERSIZE 0x100000
static u32 *SOC_buffer = NULL;
static bool rd7i_idb_fp = false; static bool rd7i_idb_fp = false;
void KillIdbServer() { void KillIdbServer() {
rd7i_idb_fp = true; rd7i_idb_fp = true;
rd7i_idb_server.join(100); rd7i_idb_server.join(100);
socExit(); rd7i_soc_deinit();
} }
void ServerThread(RenderD7::Parameter param) { void ServerThread(RenderD7::Parameter param) {
Result ret; if (rd7i_soc_init()) return;
SOC_buffer = (u32 *)memalign(SOC_ALIGN, SOC_BUFFERSIZE);
if (SOC_buffer == NULL) {
return;
}
// Now intialise soc:u service
if ((ret = socInit(SOC_buffer, SOC_BUFFERSIZE)) != 0) {
return;
}
rd7i_idb_running = true; rd7i_idb_running = true;
rd7i_idb_fp = false; rd7i_idb_fp = false;
atexit(KillIdbServer); atexit(KillIdbServer);

View File

@ -451,7 +451,8 @@ int RenderD7::GetRandomInt(int b, int e) {
} }
bool RenderD7::FS::FileExist(const std::string &path) { bool RenderD7::FS::FileExist(const std::string &path) {
return std::filesystem::exists(path); return std::filesystem::exists(path) &&
std::filesystem::is_regular_file(path);
} }
int RenderD7::GetFps() { return (int)rd7i_framerate; } int RenderD7::GetFps() { return (int)rd7i_framerate; }